mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
update wsh code for easier creation of client servers (for readfile/readdir/fileinfo) (#218)
This commit is contained in:
@@ -107,6 +107,17 @@ func MessageCommand(w *wshutil.WshRpc, data wshrpc.CommandMessageData, opts *wsh
|
||||
return err
|
||||
}
|
||||
|
||||
// command "remotefileinfo", wshserver.RemoteFileInfoCommand
|
||||
func RemoteFileInfoCommand(w *wshutil.WshRpc, data string, opts *wshrpc.WshRpcCommandOpts) (*wshrpc.FileInfo, error) {
|
||||
resp, err := sendRpcRequestCallHelper[*wshrpc.FileInfo](w, "remotefileinfo", data, opts)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// command "remotestreamfile", wshserver.RemoteStreamFileCommand
|
||||
func RemoteStreamFileCommand(w *wshutil.WshRpc, data wshrpc.CommandRemoteStreamFileData, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteStreamFileRtnData] {
|
||||
return sendRpcRequestResponseStreamHelper[wshrpc.CommandRemoteStreamFileRtnData](w, "remotestreamfile", data, opts)
|
||||
}
|
||||
|
||||
// command "resolveids", wshserver.ResolveIdsCommand
|
||||
func ResolveIdsCommand(w *wshutil.WshRpc, data wshrpc.CommandResolveIdsData, opts *wshrpc.WshRpcCommandOpts) (wshrpc.CommandResolveIdsRtnData, error) {
|
||||
resp, err := sendRpcRequestCallHelper[wshrpc.CommandResolveIdsRtnData](w, "resolveids", data, opts)
|
||||
@@ -140,4 +151,10 @@ func StreamWaveAiCommand(w *wshutil.WshRpc, data wshrpc.OpenAiStreamRequest, opt
|
||||
return sendRpcRequestResponseStreamHelper[wshrpc.OpenAIPacketType](w, "streamwaveai", data, opts)
|
||||
}
|
||||
|
||||
// command "test", wshserver.TestCommand
|
||||
func TestCommand(w *wshutil.WshRpc, data string, opts *wshrpc.WshRpcCommandOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "test", data, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ import (
|
||||
)
|
||||
|
||||
func sendRpcRequestCallHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.WshRpcCommandOpts) (T, error) {
|
||||
if opts == nil {
|
||||
opts = &wshrpc.WshRpcCommandOpts{}
|
||||
}
|
||||
var respData T
|
||||
if opts.NoResponse {
|
||||
err := w.SendCommand(command, data)
|
||||
@@ -30,6 +33,9 @@ func sendRpcRequestCallHelper[T any](w *wshutil.WshRpc, command string, data int
|
||||
}
|
||||
|
||||
func sendRpcRequestResponseStreamHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[T] {
|
||||
if opts == nil {
|
||||
opts = &wshrpc.WshRpcCommandOpts{}
|
||||
}
|
||||
respChan := make(chan wshrpc.RespOrErrorUnion[T])
|
||||
reqHandler, err := w.SendComplexRequest(command, data, true, opts.Timeout)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ package wshrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/wavetermdev/thenextwave/pkg/ijson"
|
||||
@@ -44,6 +45,9 @@ const (
|
||||
Command_StreamTest = "streamtest"
|
||||
Command_StreamWaveAi = "streamwaveai"
|
||||
Command_StreamCpuData = "streamcpudata"
|
||||
Command_Test = "test"
|
||||
Command_RemoteStreamFile = "remotestreamfile"
|
||||
Command_RemoteFileInfo = "remotefileinfo"
|
||||
)
|
||||
|
||||
type RespOrErrorUnion[T any] struct {
|
||||
@@ -74,6 +78,11 @@ type WshRpcInterface interface {
|
||||
StreamTestCommand(ctx context.Context) chan RespOrErrorUnion[int]
|
||||
StreamWaveAiCommand(ctx context.Context, request OpenAiStreamRequest) chan RespOrErrorUnion[OpenAIPacketType]
|
||||
StreamCpuDataCommand(ctx context.Context, request CpuDataRequest) chan RespOrErrorUnion[CpuDataType]
|
||||
TestCommand(ctx context.Context, data string) error
|
||||
|
||||
// remotes
|
||||
RemoteStreamFileCommand(ctx context.Context, data CommandRemoteStreamFileData) chan RespOrErrorUnion[CommandRemoteStreamFileRtnData]
|
||||
RemoteFileInfoCommand(ctx context.Context, path string) (*FileInfo, error)
|
||||
}
|
||||
|
||||
// for frontend
|
||||
@@ -243,3 +252,25 @@ type CpuDataType struct {
|
||||
Time int64 `json:"time"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Path string `json:"path"` // cleaned path
|
||||
Name string `json:"name"`
|
||||
NotFound bool `json:"notfound,omitempty"`
|
||||
Size int64 `json:"size"`
|
||||
Mode os.FileMode `json:"mode"`
|
||||
ModeStr string `json:"modestr"`
|
||||
ModTime int64 `json:"modtime"`
|
||||
IsDir bool `json:"isdir,omitempty"`
|
||||
MimeType string `json:"mimetype,omitempty"`
|
||||
}
|
||||
|
||||
type CommandRemoteStreamFileData struct {
|
||||
Path string `json:"path"`
|
||||
ByteRange string `json:"byterange,omitempty"`
|
||||
}
|
||||
|
||||
type CommandRemoteStreamFileRtnData struct {
|
||||
FileInfo *FileInfo `json:"fileinfo,omitempty"`
|
||||
Data64 string `json:"data64,omitempty"`
|
||||
}
|
||||
|
||||
@@ -23,12 +23,49 @@ import (
|
||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wps"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshclient"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||
)
|
||||
|
||||
const SimpleId_This = "this"
|
||||
|
||||
func (ws *WshServer) TestCommand(ctx context.Context, data string) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("panic in TestCommand: %v", r)
|
||||
}
|
||||
}()
|
||||
rpc := wshutil.GetWshRpcFromContext(ctx)
|
||||
if rpc == nil {
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
wshclient.MessageCommand(rpc, wshrpc.CommandMessageData{Message: "test message"}, &wshrpc.WshRpcCommandOpts{NoResponse: true})
|
||||
resp, err := wshclient.RemoteFileInfoCommand(rpc, "~/work/wails/thenextwave/README.md", nil)
|
||||
if err != nil {
|
||||
log.Printf("error getting remote file info: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("remote file info: %#v\n", resp)
|
||||
|
||||
rch := wshclient.RemoteStreamFileCommand(rpc, wshrpc.CommandRemoteStreamFileData{Path: "~/work/wails/thenextwave/README.md"}, nil)
|
||||
for msg := range rch {
|
||||
if msg.Error != nil {
|
||||
log.Printf("error in stream: %v", msg.Error)
|
||||
break
|
||||
}
|
||||
if msg.Response.FileInfo != nil {
|
||||
log.Printf("stream resp (fileinfo): %v\n", msg.Response.FileInfo)
|
||||
}
|
||||
if msg.Response.Data64 != "" {
|
||||
log.Printf("stream resp (data): %v\n", len(msg.Response.Data64))
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WshServer) AuthenticateCommand(ctx context.Context, data string) error {
|
||||
w := wshutil.GetWshRpcFromContext(ctx)
|
||||
if w == nil {
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||
)
|
||||
@@ -24,6 +23,8 @@ const (
|
||||
|
||||
type WshServer struct{}
|
||||
|
||||
func (*WshServer) WshServerImpl() {}
|
||||
|
||||
type WshServerMethodDecl struct {
|
||||
Command string
|
||||
CommandType string
|
||||
@@ -88,75 +89,6 @@ func decodeRtnVals(rtnVals []reflect.Value) (any, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func mainWshServerHandler(handler *wshutil.RpcResponseHandler) bool {
|
||||
command := handler.GetCommand()
|
||||
methodDecl := wshCommandDeclMap[command]
|
||||
if methodDecl == nil {
|
||||
handler.SendResponseError(fmt.Errorf("command %q not found", command))
|
||||
return true
|
||||
}
|
||||
var callParams []reflect.Value
|
||||
callParams = append(callParams, reflect.ValueOf(handler.Context()))
|
||||
if methodDecl.CommandDataType != nil {
|
||||
commandData := reflect.New(methodDecl.CommandDataType).Interface()
|
||||
err := utilfn.ReUnmarshal(commandData, handler.GetCommandRawData())
|
||||
if err != nil {
|
||||
handler.SendResponseError(fmt.Errorf("error re-marshalling command data: %w", err))
|
||||
return true
|
||||
}
|
||||
wshrpc.HackRpcContextIntoData(commandData, handler.GetRpcContext())
|
||||
callParams = append(callParams, reflect.ValueOf(commandData).Elem())
|
||||
}
|
||||
implVal := reflect.ValueOf(&WshServerImpl)
|
||||
implMethod := implVal.MethodByName(methodDecl.MethodName)
|
||||
if !implMethod.IsValid() {
|
||||
if !handler.NeedsResponse() {
|
||||
// we also send an out of band message here since this is likely unexpected and will require debugging
|
||||
handler.SendMessage(fmt.Sprintf("command %q method %q not found", handler.GetCommand(), methodDecl.MethodName))
|
||||
}
|
||||
handler.SendResponseError(fmt.Errorf("method %q not found", methodDecl.MethodName))
|
||||
return true
|
||||
}
|
||||
if methodDecl.CommandType == wshrpc.RpcType_Call {
|
||||
rtnVals := implMethod.Call(callParams)
|
||||
rtnData, rtnErr := decodeRtnVals(rtnVals)
|
||||
if rtnErr != nil {
|
||||
handler.SendResponseError(rtnErr)
|
||||
return true
|
||||
}
|
||||
handler.SendResponse(rtnData, true)
|
||||
return true
|
||||
} else if methodDecl.CommandType == wshrpc.RpcType_ResponseStream {
|
||||
rtnVals := implMethod.Call(callParams)
|
||||
rtnChVal := rtnVals[0]
|
||||
if rtnChVal.IsNil() {
|
||||
handler.SendResponse(nil, true)
|
||||
return true
|
||||
}
|
||||
go func() {
|
||||
defer handler.Finalize()
|
||||
// must use reflection here because we don't know the generic type of RespOrErrorUnion
|
||||
for {
|
||||
respVal, ok := rtnChVal.Recv()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
errorVal := respVal.FieldByName("Error")
|
||||
if !errorVal.IsNil() {
|
||||
handler.SendResponseError(errorVal.Interface().(error))
|
||||
break
|
||||
}
|
||||
respData := respVal.FieldByName("Response").Interface()
|
||||
handler.SendResponse(respData, false)
|
||||
}
|
||||
}()
|
||||
return false
|
||||
} else {
|
||||
handler.SendResponseError(fmt.Errorf("unsupported command type %q", methodDecl.CommandType))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func RunWshRpcOverListener(listener net.Listener) {
|
||||
defer log.Printf("domain socket listener shutting down\n")
|
||||
for {
|
||||
@@ -167,10 +99,10 @@ func RunWshRpcOverListener(listener net.Listener) {
|
||||
}
|
||||
log.Print("got domain socket connection\n")
|
||||
// TODO deal with closing connection
|
||||
go wshutil.SetupConnRpcClient(conn, mainWshServerHandler)
|
||||
go wshutil.SetupConnRpcClient(conn, &WshServerImpl)
|
||||
}
|
||||
}
|
||||
|
||||
func MakeWshServer(inputCh chan []byte, outputCh chan []byte, initialCtx wshrpc.RpcContext) {
|
||||
wshutil.MakeWshRpc(inputCh, outputCh, initialCtx, mainWshServerHandler)
|
||||
wshutil.MakeWshRpc(inputCh, outputCh, initialCtx, &WshServerImpl)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user