mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
wsh routing + proxy (#224)
lots of changes, including: * source/route to rpcmessage * rpcproxy * wshrouter * bug fixing * wps uses routeids not clients
This commit is contained in:
parent
01d788b780
commit
844451ea0d
@ -23,7 +23,7 @@ func genMethod_ResponseStream(fd *os.File, methodDecl *wshrpc.WshRpcMethodDecl)
|
|||||||
if methodDecl.DefaultResponseDataType != nil {
|
if methodDecl.DefaultResponseDataType != nil {
|
||||||
respType = methodDecl.DefaultResponseDataType.String()
|
respType = methodDecl.DefaultResponseDataType.String()
|
||||||
}
|
}
|
||||||
fmt.Fprintf(fd, "func %s(w *wshutil.WshRpc%s, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[%s] {\n", methodDecl.MethodName, dataType, respType)
|
fmt.Fprintf(fd, "func %s(w *wshutil.WshRpc%s, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[%s] {\n", methodDecl.MethodName, dataType, respType)
|
||||||
fmt.Fprintf(fd, " return sendRpcRequestResponseStreamHelper[%s](w, %q, %s, opts)\n", respType, methodDecl.Command, dataVarName)
|
fmt.Fprintf(fd, " return sendRpcRequestResponseStreamHelper[%s](w, %q, %s, opts)\n", respType, methodDecl.Command, dataVarName)
|
||||||
fmt.Fprintf(fd, "}\n\n")
|
fmt.Fprintf(fd, "}\n\n")
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ func genMethod_Call(fd *os.File, methodDecl *wshrpc.WshRpcMethodDecl) {
|
|||||||
respName = "resp"
|
respName = "resp"
|
||||||
tParamVal = methodDecl.DefaultResponseDataType.String()
|
tParamVal = methodDecl.DefaultResponseDataType.String()
|
||||||
}
|
}
|
||||||
fmt.Fprintf(fd, "func %s(w *wshutil.WshRpc%s, opts *wshrpc.WshRpcCommandOpts) %s {\n", methodDecl.MethodName, dataType, returnType)
|
fmt.Fprintf(fd, "func %s(w *wshutil.WshRpc%s, opts *wshrpc.RpcOpts) %s {\n", methodDecl.MethodName, dataType, returnType)
|
||||||
fmt.Fprintf(fd, " %s, err := sendRpcRequestCallHelper[%s](w, %q, %s, opts)\n", respName, tParamVal, methodDecl.Command, dataVarName)
|
fmt.Fprintf(fd, " %s, err := sendRpcRequestCallHelper[%s](w, %q, %s, opts)\n", respName, tParamVal, methodDecl.Command, dataVarName)
|
||||||
if methodDecl.DefaultResponseDataType != nil {
|
if methodDecl.DefaultResponseDataType != nil {
|
||||||
fmt.Fprintf(fd, " return resp, err\n")
|
fmt.Fprintf(fd, " return resp, err\n")
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/service"
|
"github.com/wavetermdev/thenextwave/pkg/service"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
||||||
@ -27,6 +26,7 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/wconfig"
|
"github.com/wavetermdev/thenextwave/pkg/wconfig"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/web"
|
"github.com/wavetermdev/thenextwave/pkg/web"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshserver"
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshserver"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,11 +147,15 @@ func shutdownActivityUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createMainWshClient() {
|
||||||
|
rpc := wshserver.GetMainRpcClient()
|
||||||
|
wshutil.DefaultRouter.RegisterRoute("wavesrv", rpc)
|
||||||
|
wshutil.DefaultRouter.SetDefaultRoute("wavesrv")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||||
log.SetPrefix("[wavesrv] ")
|
log.SetPrefix("[wavesrv] ")
|
||||||
blockcontroller.WshServerFactoryFn = wshserver.MakeWshServer
|
|
||||||
web.WshServerFactoryFn = wshserver.MakeWshServer
|
|
||||||
wavebase.WaveVersion = WaveVersion
|
wavebase.WaveVersion = WaveVersion
|
||||||
wavebase.BuildTime = BuildTime
|
wavebase.BuildTime = BuildTime
|
||||||
|
|
||||||
@ -200,6 +204,7 @@ func main() {
|
|||||||
log.Printf("error ensuring initial data: %v\n", err)
|
log.Printf("error ensuring initial data: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
createMainWshClient()
|
||||||
installShutdownSignalHandlers()
|
installShutdownSignalHandlers()
|
||||||
startupActivityUpdate()
|
startupActivityUpdate()
|
||||||
go stdinReadWatch()
|
go stdinReadWatch()
|
||||||
|
@ -42,7 +42,7 @@ func deleteBlockRun(cmd *cobra.Command, args []string) {
|
|||||||
deleteBlockData := &wshrpc.CommandDeleteBlockData{
|
deleteBlockData := &wshrpc.CommandDeleteBlockData{
|
||||||
BlockId: fullORef.OID,
|
BlockId: fullORef.OID,
|
||||||
}
|
}
|
||||||
_, err = RpcClient.SendRpcRequest(wshrpc.Command_DeleteBlock, deleteBlockData, 2000)
|
_, err = RpcClient.SendRpcRequest(wshrpc.Command_DeleteBlock, deleteBlockData, &wshrpc.RpcOpts{Timeout: 2000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteStderr("[error] deleting block: %v\n", err)
|
WriteStderr("[error] deleting block: %v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -38,7 +38,7 @@ func getMetaRun(cmd *cobra.Command, args []string) {
|
|||||||
WriteStderr("[error] resolving oref: %v\n", err)
|
WriteStderr("[error] resolving oref: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := wshclient.GetMetaCommand(RpcClient, wshrpc.CommandGetMetaData{ORef: *fullORef}, &wshrpc.WshRpcCommandOpts{Timeout: 2000})
|
resp, err := wshclient.GetMetaCommand(RpcClient, wshrpc.CommandGetMetaData{ORef: *fullORef}, &wshrpc.RpcOpts{Timeout: 2000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteStderr("[error] getting metadata: %v\n", err)
|
WriteStderr("[error] getting metadata: %v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -38,7 +38,7 @@ func runReadFile(cmd *cobra.Command, args []string) {
|
|||||||
WriteStderr("error resolving oref: %v\n", err)
|
WriteStderr("error resolving oref: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp64, err := wshclient.FileReadCommand(RpcClient, wshrpc.CommandFileData{ZoneId: fullORef.OID, FileName: args[1]}, &wshrpc.WshRpcCommandOpts{Timeout: 5000})
|
resp64, err := wshclient.FileReadCommand(RpcClient, wshrpc.CommandFileData{ZoneId: fullORef.OID, FileName: args[1]}, &wshrpc.RpcOpts{Timeout: 5000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteStderr("[error] reading file: %v\n", err)
|
WriteStderr("[error] reading file: %v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -39,7 +39,7 @@ func extraShutdownFn() {
|
|||||||
cmd := &wshrpc.CommandSetMetaData{
|
cmd := &wshrpc.CommandSetMetaData{
|
||||||
Meta: map[string]any{"term:mode": nil},
|
Meta: map[string]any{"term:mode": nil},
|
||||||
}
|
}
|
||||||
RpcClient.SendCommand(wshrpc.Command_SetMeta, cmd)
|
RpcClient.SendCommand(wshrpc.Command_SetMeta, cmd, nil)
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ func setupRpcClient(serverImpl wshutil.ServerImpl) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error setting up domain socket rpc client: %v", err)
|
return fmt.Errorf("error setting up domain socket rpc client: %v", err)
|
||||||
}
|
}
|
||||||
wshclient.AuthenticateCommand(RpcClient, jwtToken, &wshrpc.WshRpcCommandOpts{NoResponse: true})
|
wshclient.AuthenticateCommand(RpcClient, jwtToken, &wshrpc.RpcOpts{NoResponse: true})
|
||||||
// note we don't modify WrappedStdin here (just use os.Stdin)
|
// note we don't modify WrappedStdin here (just use os.Stdin)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ func setTermHtmlMode() {
|
|||||||
cmd := &wshrpc.CommandSetMetaData{
|
cmd := &wshrpc.CommandSetMetaData{
|
||||||
Meta: map[string]any{"term:mode": "html"},
|
Meta: map[string]any{"term:mode": "html"},
|
||||||
}
|
}
|
||||||
err := RpcClient.SendCommand(wshrpc.Command_SetMeta, cmd)
|
err := RpcClient.SendCommand(wshrpc.Command_SetMeta, cmd, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error setting html mode: %v\r\n", err)
|
fmt.Fprintf(os.Stderr, "Error setting html mode: %v\r\n", err)
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ func resolveSimpleId(id string) (*waveobj.ORef, error) {
|
|||||||
}
|
}
|
||||||
return &orefObj, nil
|
return &orefObj, nil
|
||||||
}
|
}
|
||||||
rtnData, err := wshclient.ResolveIdsCommand(RpcClient, wshrpc.CommandResolveIdsData{Ids: []string{id}}, &wshrpc.WshRpcCommandOpts{Timeout: 2000})
|
rtnData, err := wshclient.ResolveIdsCommand(RpcClient, wshrpc.CommandResolveIdsData{Ids: []string{id}}, &wshrpc.RpcOpts{Timeout: 2000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error resolving ids: %v", err)
|
return nil, fmt.Errorf("error resolving ids: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func setMetaRun(cmd *cobra.Command, args []string) {
|
|||||||
ORef: *fullORef,
|
ORef: *fullORef,
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
}
|
}
|
||||||
_, err = RpcClient.SendRpcRequest(wshrpc.Command_SetMeta, setMetaWshCmd, 2000)
|
_, err = RpcClient.SendRpcRequest(wshrpc.Command_SetMeta, setMetaWshCmd, &wshrpc.RpcOpts{Timeout: 2000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteStderr("[error] setting metadata: %v\n", err)
|
WriteStderr("[error] setting metadata: %v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -64,7 +64,7 @@ func viewRun(cmd *cobra.Command, args []string) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err := RpcClient.SendRpcRequest(wshrpc.Command_CreateBlock, wshCmd, 2000)
|
_, err := RpcClient.SendRpcRequest(wshrpc.Command_CreateBlock, wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteStderr("[error] running view command: %v\r\n", err)
|
WriteStderr("[error] running view command: %v\r\n", err)
|
||||||
return
|
return
|
||||||
|
@ -111,7 +111,7 @@ function callBackendService(service: string, method: string, args: any[], noUICo
|
|||||||
function wshServerRpcHelper_responsestream(
|
function wshServerRpcHelper_responsestream(
|
||||||
command: string,
|
command: string,
|
||||||
data: any,
|
data: any,
|
||||||
opts: WshRpcCommandOpts
|
opts: RpcOpts
|
||||||
): AsyncGenerator<any, void, boolean> {
|
): AsyncGenerator<any, void, boolean> {
|
||||||
if (opts?.noresponse) {
|
if (opts?.noresponse) {
|
||||||
throw new Error("noresponse not supported for responsestream calls");
|
throw new Error("noresponse not supported for responsestream calls");
|
||||||
@ -124,11 +124,14 @@ function wshServerRpcHelper_responsestream(
|
|||||||
if (opts?.timeout) {
|
if (opts?.timeout) {
|
||||||
msg.timeout = opts.timeout;
|
msg.timeout = opts.timeout;
|
||||||
}
|
}
|
||||||
|
if (opts?.route) {
|
||||||
|
msg.route = opts.route;
|
||||||
|
}
|
||||||
const rpcGen = sendRpcCommand(msg);
|
const rpcGen = sendRpcCommand(msg);
|
||||||
return rpcGen;
|
return rpcGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wshServerRpcHelper_call(command: string, data: any, opts: WshRpcCommandOpts): Promise<any> {
|
function wshServerRpcHelper_call(command: string, data: any, opts: RpcOpts): Promise<any> {
|
||||||
const msg: RpcMessage = {
|
const msg: RpcMessage = {
|
||||||
command: command,
|
command: command,
|
||||||
data: data,
|
data: data,
|
||||||
@ -139,6 +142,9 @@ function wshServerRpcHelper_call(command: string, data: any, opts: WshRpcCommand
|
|||||||
if (opts?.timeout) {
|
if (opts?.timeout) {
|
||||||
msg.timeout = opts.timeout;
|
msg.timeout = opts.timeout;
|
||||||
}
|
}
|
||||||
|
if (opts?.route) {
|
||||||
|
msg.route = opts.route;
|
||||||
|
}
|
||||||
const rpcGen = sendRpcCommand(msg);
|
const rpcGen = sendRpcCommand(msg);
|
||||||
if (rpcGen == null) {
|
if (rpcGen == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -8,127 +8,127 @@ import * as WOS from "./wos";
|
|||||||
// WshServerCommandToDeclMap
|
// WshServerCommandToDeclMap
|
||||||
class WshServerType {
|
class WshServerType {
|
||||||
// command "authenticate" [call]
|
// command "authenticate" [call]
|
||||||
AuthenticateCommand(data: string, opts?: WshRpcCommandOpts): Promise<void> {
|
AuthenticateCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("authenticate", data, opts);
|
return WOS.wshServerRpcHelper_call("authenticate", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "controllerinput" [call]
|
// command "controllerinput" [call]
|
||||||
ControllerInputCommand(data: CommandBlockInputData, opts?: WshRpcCommandOpts): Promise<void> {
|
ControllerInputCommand(data: CommandBlockInputData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("controllerinput", data, opts);
|
return WOS.wshServerRpcHelper_call("controllerinput", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "controllerrestart" [call]
|
// command "controllerrestart" [call]
|
||||||
ControllerRestartCommand(data: CommandBlockRestartData, opts?: WshRpcCommandOpts): Promise<void> {
|
ControllerRestartCommand(data: CommandBlockRestartData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("controllerrestart", data, opts);
|
return WOS.wshServerRpcHelper_call("controllerrestart", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "createblock" [call]
|
// command "createblock" [call]
|
||||||
CreateBlockCommand(data: CommandCreateBlockData, opts?: WshRpcCommandOpts): Promise<ORef> {
|
CreateBlockCommand(data: CommandCreateBlockData, opts?: RpcOpts): Promise<ORef> {
|
||||||
return WOS.wshServerRpcHelper_call("createblock", data, opts);
|
return WOS.wshServerRpcHelper_call("createblock", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "deleteblock" [call]
|
// command "deleteblock" [call]
|
||||||
DeleteBlockCommand(data: CommandDeleteBlockData, opts?: WshRpcCommandOpts): Promise<void> {
|
DeleteBlockCommand(data: CommandDeleteBlockData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("deleteblock", data, opts);
|
return WOS.wshServerRpcHelper_call("deleteblock", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventpublish" [call]
|
// command "eventpublish" [call]
|
||||||
EventPublishCommand(data: WaveEvent, opts?: WshRpcCommandOpts): Promise<void> {
|
EventPublishCommand(data: WaveEvent, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("eventpublish", data, opts);
|
return WOS.wshServerRpcHelper_call("eventpublish", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventrecv" [call]
|
// command "eventrecv" [call]
|
||||||
EventRecvCommand(data: WaveEvent, opts?: WshRpcCommandOpts): Promise<void> {
|
EventRecvCommand(data: WaveEvent, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("eventrecv", data, opts);
|
return WOS.wshServerRpcHelper_call("eventrecv", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventsub" [call]
|
// command "eventsub" [call]
|
||||||
EventSubCommand(data: SubscriptionRequest, opts?: WshRpcCommandOpts): Promise<void> {
|
EventSubCommand(data: SubscriptionRequest, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("eventsub", data, opts);
|
return WOS.wshServerRpcHelper_call("eventsub", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventunsub" [call]
|
// command "eventunsub" [call]
|
||||||
EventUnsubCommand(data: SubscriptionRequest, opts?: WshRpcCommandOpts): Promise<void> {
|
EventUnsubCommand(data: SubscriptionRequest, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("eventunsub", data, opts);
|
return WOS.wshServerRpcHelper_call("eventunsub", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventunsuball" [call]
|
// command "eventunsuball" [call]
|
||||||
EventUnsubAllCommand(opts?: WshRpcCommandOpts): Promise<void> {
|
EventUnsubAllCommand(opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("eventunsuball", null, opts);
|
return WOS.wshServerRpcHelper_call("eventunsuball", null, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileappend" [call]
|
// command "fileappend" [call]
|
||||||
FileAppendCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
FileAppendCommand(data: CommandFileData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("fileappend", data, opts);
|
return WOS.wshServerRpcHelper_call("fileappend", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileappendijson" [call]
|
// command "fileappendijson" [call]
|
||||||
FileAppendIJsonCommand(data: CommandAppendIJsonData, opts?: WshRpcCommandOpts): Promise<void> {
|
FileAppendIJsonCommand(data: CommandAppendIJsonData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("fileappendijson", data, opts);
|
return WOS.wshServerRpcHelper_call("fileappendijson", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileread" [call]
|
// command "fileread" [call]
|
||||||
FileReadCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<string> {
|
FileReadCommand(data: CommandFileData, opts?: RpcOpts): Promise<string> {
|
||||||
return WOS.wshServerRpcHelper_call("fileread", data, opts);
|
return WOS.wshServerRpcHelper_call("fileread", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "filewrite" [call]
|
// command "filewrite" [call]
|
||||||
FileWriteCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
FileWriteCommand(data: CommandFileData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("filewrite", data, opts);
|
return WOS.wshServerRpcHelper_call("filewrite", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "getmeta" [call]
|
// command "getmeta" [call]
|
||||||
GetMetaCommand(data: CommandGetMetaData, opts?: WshRpcCommandOpts): Promise<MetaType> {
|
GetMetaCommand(data: CommandGetMetaData, opts?: RpcOpts): Promise<MetaType> {
|
||||||
return WOS.wshServerRpcHelper_call("getmeta", data, opts);
|
return WOS.wshServerRpcHelper_call("getmeta", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "message" [call]
|
// command "message" [call]
|
||||||
MessageCommand(data: CommandMessageData, opts?: WshRpcCommandOpts): Promise<void> {
|
MessageCommand(data: CommandMessageData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("message", data, opts);
|
return WOS.wshServerRpcHelper_call("message", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "remotefileinfo" [call]
|
// command "remotefileinfo" [call]
|
||||||
RemoteFileInfoCommand(data: string, opts?: WshRpcCommandOpts): Promise<FileInfo> {
|
RemoteFileInfoCommand(data: string, opts?: RpcOpts): Promise<FileInfo> {
|
||||||
return WOS.wshServerRpcHelper_call("remotefileinfo", data, opts);
|
return WOS.wshServerRpcHelper_call("remotefileinfo", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "remotestreamfile" [responsestream]
|
// command "remotestreamfile" [responsestream]
|
||||||
RemoteStreamFileCommand(data: CommandRemoteStreamFileData, opts?: WshRpcCommandOpts): AsyncGenerator<CommandRemoteStreamFileRtnData, void, boolean> {
|
RemoteStreamFileCommand(data: CommandRemoteStreamFileData, opts?: RpcOpts): AsyncGenerator<CommandRemoteStreamFileRtnData, void, boolean> {
|
||||||
return WOS.wshServerRpcHelper_responsestream("remotestreamfile", data, opts);
|
return WOS.wshServerRpcHelper_responsestream("remotestreamfile", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "resolveids" [call]
|
// command "resolveids" [call]
|
||||||
ResolveIdsCommand(data: CommandResolveIdsData, opts?: WshRpcCommandOpts): Promise<CommandResolveIdsRtnData> {
|
ResolveIdsCommand(data: CommandResolveIdsData, opts?: RpcOpts): Promise<CommandResolveIdsRtnData> {
|
||||||
return WOS.wshServerRpcHelper_call("resolveids", data, opts);
|
return WOS.wshServerRpcHelper_call("resolveids", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "setmeta" [call]
|
// command "setmeta" [call]
|
||||||
SetMetaCommand(data: CommandSetMetaData, opts?: WshRpcCommandOpts): Promise<void> {
|
SetMetaCommand(data: CommandSetMetaData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("setmeta", data, opts);
|
return WOS.wshServerRpcHelper_call("setmeta", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "setview" [call]
|
// command "setview" [call]
|
||||||
SetViewCommand(data: CommandBlockSetViewData, opts?: WshRpcCommandOpts): Promise<void> {
|
SetViewCommand(data: CommandBlockSetViewData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("setview", data, opts);
|
return WOS.wshServerRpcHelper_call("setview", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamcpudata" [responsestream]
|
// command "streamcpudata" [responsestream]
|
||||||
StreamCpuDataCommand(data: CpuDataRequest, opts?: WshRpcCommandOpts): AsyncGenerator<CpuDataType, void, boolean> {
|
StreamCpuDataCommand(data: CpuDataRequest, opts?: RpcOpts): AsyncGenerator<CpuDataType, void, boolean> {
|
||||||
return WOS.wshServerRpcHelper_responsestream("streamcpudata", data, opts);
|
return WOS.wshServerRpcHelper_responsestream("streamcpudata", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamtest" [responsestream]
|
// command "streamtest" [responsestream]
|
||||||
StreamTestCommand(opts?: WshRpcCommandOpts): AsyncGenerator<number, void, boolean> {
|
StreamTestCommand(opts?: RpcOpts): AsyncGenerator<number, void, boolean> {
|
||||||
return WOS.wshServerRpcHelper_responsestream("streamtest", null, opts);
|
return WOS.wshServerRpcHelper_responsestream("streamtest", null, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamwaveai" [responsestream]
|
// command "streamwaveai" [responsestream]
|
||||||
StreamWaveAiCommand(data: OpenAiStreamRequest, opts?: WshRpcCommandOpts): AsyncGenerator<OpenAIPacketType, void, boolean> {
|
StreamWaveAiCommand(data: OpenAiStreamRequest, opts?: RpcOpts): AsyncGenerator<OpenAIPacketType, void, boolean> {
|
||||||
return WOS.wshServerRpcHelper_responsestream("streamwaveai", data, opts);
|
return WOS.wshServerRpcHelper_responsestream("streamwaveai", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "test" [call]
|
// command "test" [call]
|
||||||
TestCommand(data: string, opts?: WshRpcCommandOpts): Promise<void> {
|
TestCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("test", data, opts);
|
return WOS.wshServerRpcHelper_call("test", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
frontend/types/gotypes.d.ts
vendored
16
frontend/types/gotypes.d.ts
vendored
@ -130,6 +130,7 @@ declare global {
|
|||||||
|
|
||||||
// wshrpc.CommandResolveIdsData
|
// wshrpc.CommandResolveIdsData
|
||||||
type CommandResolveIdsData = {
|
type CommandResolveIdsData = {
|
||||||
|
blockid: string;
|
||||||
ids: string[];
|
ids: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -305,6 +306,8 @@ declare global {
|
|||||||
reqid?: string;
|
reqid?: string;
|
||||||
resid?: string;
|
resid?: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
route?: string;
|
||||||
|
source?: string;
|
||||||
cont?: boolean;
|
cont?: boolean;
|
||||||
cancel?: boolean;
|
cancel?: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
@ -312,6 +315,13 @@ declare global {
|
|||||||
data?: any;
|
data?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// wshrpc.RpcOpts
|
||||||
|
type RpcOpts = {
|
||||||
|
timeout?: number;
|
||||||
|
noresponse?: boolean;
|
||||||
|
route?: string;
|
||||||
|
};
|
||||||
|
|
||||||
// wstore.RuntimeOpts
|
// wstore.RuntimeOpts
|
||||||
type RuntimeOpts = {
|
type RuntimeOpts = {
|
||||||
termsize?: TermSize;
|
termsize?: TermSize;
|
||||||
@ -607,12 +617,6 @@ declare global {
|
|||||||
tabids: string[];
|
tabids: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshrpc.WshRpcCommandOpts
|
|
||||||
type WshRpcCommandOpts = {
|
|
||||||
timeout: number;
|
|
||||||
noresponse: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshrpc.WshServerCommandMeta
|
// wshrpc.WshServerCommandMeta
|
||||||
type WshServerCommandMeta = {
|
type WshServerCommandMeta = {
|
||||||
commandtype: string;
|
commandtype: string;
|
||||||
|
@ -27,9 +27,6 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// set by main-server.go (for dependency inversion)
|
|
||||||
var WshServerFactoryFn func(inputCh chan []byte, outputCh chan []byte, initialCtx wshrpc.RpcContext) = nil
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BlockController_Shell = "shell"
|
BlockController_Shell = "shell"
|
||||||
BlockController_Cmd = "cmd"
|
BlockController_Cmd = "cmd"
|
||||||
@ -327,10 +324,13 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
|||||||
})
|
})
|
||||||
shellInputCh := make(chan *BlockInputUnion, 32)
|
shellInputCh := make(chan *BlockInputUnion, 32)
|
||||||
bc.ShellInputCh = shellInputCh
|
bc.ShellInputCh = shellInputCh
|
||||||
messageCh := make(chan []byte, 32)
|
|
||||||
ptyBuffer := wshutil.MakePtyBuffer(wshutil.WaveOSCPrefix, bc.ShellProc.Cmd, messageCh)
|
// make esc sequence wshclient wshProxy
|
||||||
outputCh := make(chan []byte, 32)
|
// we don't need to authenticate this wshProxy since it is coming direct
|
||||||
WshServerFactoryFn(messageCh, outputCh, wshrpc.RpcContext{BlockId: bc.BlockId, TabId: bc.TabId})
|
wshProxy := wshutil.MakeRpcProxy()
|
||||||
|
wshProxy.SetRpcContext(&wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId})
|
||||||
|
wshutil.DefaultRouter.RegisterRoute("controller:"+bc.BlockId, wshProxy)
|
||||||
|
ptyBuffer := wshutil.MakePtyBuffer(wshutil.WaveOSCPrefix, bc.ShellProc.Cmd, wshProxy.FromRemoteCh)
|
||||||
go func() {
|
go func() {
|
||||||
// handles regular output from the pty (goes to the blockfile and xterm)
|
// handles regular output from the pty (goes to the blockfile and xterm)
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -380,7 +380,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
|||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
// handles outputCh -> shellInputCh
|
// handles outputCh -> shellInputCh
|
||||||
for msg := range outputCh {
|
for msg := range wshProxy.ToRemoteCh {
|
||||||
encodedMsg := wshutil.EncodeWaveOSCBytes(wshutil.WaveServerOSC, msg)
|
encodedMsg := wshutil.EncodeWaveOSCBytes(wshutil.WaveServerOSC, msg)
|
||||||
shellInputCh <- &BlockInputUnion{InputData: encodedMsg}
|
shellInputCh <- &BlockInputUnion{InputData: encodedMsg}
|
||||||
}
|
}
|
||||||
@ -388,6 +388,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
|||||||
go func() {
|
go func() {
|
||||||
// wait for the shell to finish
|
// wait for the shell to finish
|
||||||
defer func() {
|
defer func() {
|
||||||
|
wshutil.DefaultRouter.UnregisterRoute("controller:" + bc.BlockId)
|
||||||
bc.UpdateControllerAndSendUpdate(func() bool {
|
bc.UpdateControllerAndSendUpdate(func() bool {
|
||||||
bc.ShellProcStatus = Status_Done
|
bc.ShellProcStatus = Status_Done
|
||||||
return true
|
return true
|
||||||
|
@ -425,9 +425,9 @@ func GenerateWshServerMethod_ResponseStream(methodDecl *wshrpc.WshRpcMethodDecl,
|
|||||||
}
|
}
|
||||||
genRespType := fmt.Sprintf("AsyncGenerator<%s, void, boolean>", respType)
|
genRespType := fmt.Sprintf("AsyncGenerator<%s, void, boolean>", respType)
|
||||||
if methodDecl.CommandDataType != nil {
|
if methodDecl.CommandDataType != nil {
|
||||||
sb.WriteString(fmt.Sprintf(" %s(data: %s, opts?: WshRpcCommandOpts): %s {\n", methodDecl.MethodName, methodDecl.CommandDataType.Name(), genRespType))
|
sb.WriteString(fmt.Sprintf(" %s(data: %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, methodDecl.CommandDataType.Name(), genRespType))
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf(" %s(opts?: WshRpcCommandOpts): %s {\n", methodDecl.MethodName, genRespType))
|
sb.WriteString(fmt.Sprintf(" %s(opts?: RpcOpts): %s {\n", methodDecl.MethodName, genRespType))
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf(" return WOS.wshServerRpcHelper_responsestream(%q, %s, opts);\n", methodDecl.Command, dataName))
|
sb.WriteString(fmt.Sprintf(" return WOS.wshServerRpcHelper_responsestream(%q, %s, opts);\n", methodDecl.Command, dataName))
|
||||||
sb.WriteString(" }\n")
|
sb.WriteString(" }\n")
|
||||||
@ -447,9 +447,9 @@ func GenerateWshServerMethod_Call(methodDecl *wshrpc.WshRpcMethodDecl, tsTypesMa
|
|||||||
dataName = "data"
|
dataName = "data"
|
||||||
}
|
}
|
||||||
if methodDecl.CommandDataType != nil {
|
if methodDecl.CommandDataType != nil {
|
||||||
sb.WriteString(fmt.Sprintf(" %s(data: %s, opts?: WshRpcCommandOpts): %s {\n", methodDecl.MethodName, methodDecl.CommandDataType.Name(), rtnType))
|
sb.WriteString(fmt.Sprintf(" %s(data: %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, methodDecl.CommandDataType.Name(), rtnType))
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf(" %s(opts?: WshRpcCommandOpts): %s {\n", methodDecl.MethodName, rtnType))
|
sb.WriteString(fmt.Sprintf(" %s(opts?: RpcOpts): %s {\n", methodDecl.MethodName, rtnType))
|
||||||
}
|
}
|
||||||
methodBody := fmt.Sprintf(" return WOS.wshServerRpcHelper_call(%q, %s, opts);\n", methodDecl.Command, dataName)
|
methodBody := fmt.Sprintf(" return WOS.wshServerRpcHelper_call(%q, %s, opts);\n", methodDecl.Command, dataName)
|
||||||
sb.WriteString(methodBody)
|
sb.WriteString(methodBody)
|
||||||
@ -484,7 +484,7 @@ func GenerateServiceTypes(tsTypesMap map[reflect.Type]string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GenerateWshServerTypes(tsTypesMap map[reflect.Type]string) error {
|
func GenerateWshServerTypes(tsTypesMap map[reflect.Type]string) error {
|
||||||
GenerateTSType(reflect.TypeOf(wshrpc.WshRpcCommandOpts{}), tsTypesMap)
|
GenerateTSType(reflect.TypeOf(wshrpc.RpcOpts{}), tsTypesMap)
|
||||||
rtype := wshRpcInterfaceRType
|
rtype := wshRpcInterfaceRType
|
||||||
for midx := 0; midx < rtype.NumMethod(); midx++ {
|
for midx := 0; midx < rtype.NumMethod(); midx++ {
|
||||||
method := rtype.Method(midx)
|
method := rtype.Method(midx)
|
||||||
|
@ -22,9 +22,6 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// set by main-server.go (for dependency inversion)
|
|
||||||
var WshServerFactoryFn func(inputCh chan []byte, outputCh chan []byte, initialCtx wshrpc.RpcContext) = nil
|
|
||||||
|
|
||||||
const wsReadWaitTimeout = 15 * time.Second
|
const wsReadWaitTimeout = 15 * time.Second
|
||||||
const wsWriteWaitTimeout = 10 * time.Second
|
const wsWriteWaitTimeout = 10 * time.Second
|
||||||
const wsPingPeriodTickTime = 10 * time.Second
|
const wsPingPeriodTickTime = 10 * time.Second
|
||||||
@ -148,31 +145,10 @@ func processWSCommand(jmsg map[string]any, outputCh chan any, rpcInputCh chan []
|
|||||||
|
|
||||||
func processMessage(jmsg map[string]any, outputCh chan any, rpcInputCh chan []byte) {
|
func processMessage(jmsg map[string]any, outputCh chan any, rpcInputCh chan []byte) {
|
||||||
wsCommand := getStringFromMap(jmsg, "wscommand")
|
wsCommand := getStringFromMap(jmsg, "wscommand")
|
||||||
if wsCommand != "" {
|
if wsCommand == "" {
|
||||||
processWSCommand(jmsg, outputCh, rpcInputCh)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msgType := getMessageType(jmsg)
|
processWSCommand(jmsg, outputCh, rpcInputCh)
|
||||||
if msgType != "rpc" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reqId := getStringFromMap(jmsg, "reqid")
|
|
||||||
var rtnErr error
|
|
||||||
defer func() {
|
|
||||||
r := recover()
|
|
||||||
if r != nil {
|
|
||||||
rtnErr = fmt.Errorf("panic: %v", r)
|
|
||||||
log.Printf("panic in processMessage: %v\n", r)
|
|
||||||
debug.PrintStack()
|
|
||||||
}
|
|
||||||
if rtnErr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rtn := map[string]any{"type": "rpcresp", "reqid": reqId, "error": rtnErr.Error()}
|
|
||||||
outputCh <- rtn
|
|
||||||
}()
|
|
||||||
method := getStringFromMap(jmsg, "method")
|
|
||||||
rtnErr = fmt.Errorf("unknown method %q", method)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadLoop(conn *websocket.Conn, outputCh chan any, closeCh chan any, rpcInputCh chan []byte) {
|
func ReadLoop(conn *websocket.Conn, outputCh chan any, closeCh chan any, rpcInputCh chan []byte) {
|
||||||
@ -277,17 +253,23 @@ func HandleWsInternal(w http.ResponseWriter, r *http.Request) error {
|
|||||||
log.Printf("New websocket connection: windowid:%s connid:%s\n", windowId, wsConnId)
|
log.Printf("New websocket connection: windowid:%s connid:%s\n", windowId, wsConnId)
|
||||||
outputCh := make(chan any, 100)
|
outputCh := make(chan any, 100)
|
||||||
closeCh := make(chan any)
|
closeCh := make(chan any)
|
||||||
rpcInputCh := make(chan []byte, 32)
|
|
||||||
rpcOutputCh := make(chan []byte, 32)
|
|
||||||
eventbus.RegisterWSChannel(wsConnId, windowId, outputCh)
|
eventbus.RegisterWSChannel(wsConnId, windowId, outputCh)
|
||||||
defer eventbus.UnregisterWSChannel(wsConnId)
|
defer eventbus.UnregisterWSChannel(wsConnId)
|
||||||
WshServerFactoryFn(rpcInputCh, rpcOutputCh, wshrpc.RpcContext{WindowId: windowId})
|
// we create a wshproxy to handle rpc messages to/from the window
|
||||||
|
wproxy := wshutil.MakeRpcProxy()
|
||||||
|
rpcRouteId := "window:" + windowId
|
||||||
|
wshutil.DefaultRouter.RegisterRoute(rpcRouteId, wproxy)
|
||||||
|
defer func() {
|
||||||
|
wshutil.DefaultRouter.UnregisterRoute(rpcRouteId)
|
||||||
|
close(wproxy.ToRemoteCh)
|
||||||
|
}()
|
||||||
|
// WshServerFactoryFn(rpcInputCh, rpcOutputCh, wshrpc.RpcContext{})
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
// no waitgroup add here
|
// no waitgroup add here
|
||||||
// move values from rpcOutputCh to outputCh
|
// move values from rpcOutputCh to outputCh
|
||||||
for msgBytes := range rpcOutputCh {
|
for msgBytes := range wproxy.ToRemoteCh {
|
||||||
rpcWSMsg := map[string]any{
|
rpcWSMsg := map[string]any{
|
||||||
"eventtype": "rpc", // TODO don't hard code this (but def is in eventbus)
|
"eventtype": "rpc", // TODO don't hard code this (but def is in eventbus)
|
||||||
"data": json.RawMessage(msgBytes),
|
"data": json.RawMessage(msgBytes),
|
||||||
@ -298,7 +280,7 @@ func HandleWsInternal(w http.ResponseWriter, r *http.Request) error {
|
|||||||
go func() {
|
go func() {
|
||||||
// read loop
|
// read loop
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
ReadLoop(conn, outputCh, closeCh, rpcInputCh)
|
ReadLoop(conn, outputCh, closeCh, wproxy.FromRemoteCh)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
// write loop
|
// write loop
|
||||||
@ -306,6 +288,6 @@ func HandleWsInternal(w http.ResponseWriter, r *http.Request) error {
|
|||||||
WriteLoop(conn, outputCh, closeCh)
|
WriteLoop(conn, outputCh, closeCh)
|
||||||
}()
|
}()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(rpcInputCh)
|
close(wproxy.FromRemoteCh)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
105
pkg/wps/wps.go
105
pkg/wps/wps.go
@ -16,26 +16,24 @@ import (
|
|||||||
// strong typing and event types can be defined elsewhere
|
// strong typing and event types can be defined elsewhere
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
ClientId() string
|
SendEvent(routeId string, event wshrpc.WaveEvent)
|
||||||
SendEvent(event wshrpc.WaveEvent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BrokerSubscription struct {
|
type BrokerSubscription struct {
|
||||||
AllSubs []string // clientids of client subscribed to "all" events
|
AllSubs []string // routeids subscribed to "all" events
|
||||||
ScopeSubs map[string][]string // clientids of client subscribed to specific scopes
|
ScopeSubs map[string][]string // routeids subscribed to specific scopes
|
||||||
StarSubs map[string][]string // clientids of client subscribed to star scope (scopes with "*" or "**" in them)
|
StarSubs map[string][]string // routeids subscribed to star scope (scopes with "*" or "**" in them)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BrokerType struct {
|
type BrokerType struct {
|
||||||
Lock *sync.Mutex
|
Lock *sync.Mutex
|
||||||
ClientMap map[string]Client
|
Client Client
|
||||||
SubMap map[string]*BrokerSubscription
|
SubMap map[string]*BrokerSubscription
|
||||||
}
|
}
|
||||||
|
|
||||||
var Broker = &BrokerType{
|
var Broker = &BrokerType{
|
||||||
Lock: &sync.Mutex{},
|
Lock: &sync.Mutex{},
|
||||||
ClientMap: make(map[string]Client),
|
SubMap: make(map[string]*BrokerSubscription),
|
||||||
SubMap: make(map[string]*BrokerSubscription),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func scopeHasStarMatch(scope string) bool {
|
func scopeHasStarMatch(scope string) bool {
|
||||||
@ -48,10 +46,21 @@ func scopeHasStarMatch(scope string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrokerType) Subscribe(subscriber Client, sub wshrpc.SubscriptionRequest) {
|
func (b *BrokerType) SetClient(client Client) {
|
||||||
|
b.Lock.Lock()
|
||||||
|
defer b.Lock.Unlock()
|
||||||
|
b.Client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BrokerType) GetClient() Client {
|
||||||
|
b.Lock.Lock()
|
||||||
|
defer b.Lock.Unlock()
|
||||||
|
return b.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BrokerType) Subscribe(subRouteId string, sub wshrpc.SubscriptionRequest) {
|
||||||
b.Lock.Lock()
|
b.Lock.Lock()
|
||||||
defer b.Lock.Unlock()
|
defer b.Lock.Unlock()
|
||||||
clientId := subscriber.ClientId()
|
|
||||||
bs := b.SubMap[sub.Event]
|
bs := b.SubMap[sub.Event]
|
||||||
if bs == nil {
|
if bs == nil {
|
||||||
bs = &BrokerSubscription{
|
bs = &BrokerSubscription{
|
||||||
@ -62,14 +71,14 @@ func (b *BrokerType) Subscribe(subscriber Client, sub wshrpc.SubscriptionRequest
|
|||||||
b.SubMap[sub.Event] = bs
|
b.SubMap[sub.Event] = bs
|
||||||
}
|
}
|
||||||
if sub.AllScopes {
|
if sub.AllScopes {
|
||||||
bs.AllSubs = utilfn.AddElemToSliceUniq(bs.AllSubs, clientId)
|
bs.AllSubs = utilfn.AddElemToSliceUniq(bs.AllSubs, subRouteId)
|
||||||
}
|
}
|
||||||
for _, scope := range sub.Scopes {
|
for _, scope := range sub.Scopes {
|
||||||
starMatch := scopeHasStarMatch(scope)
|
starMatch := scopeHasStarMatch(scope)
|
||||||
if starMatch {
|
if starMatch {
|
||||||
addStrToScopeMap(bs.StarSubs, scope, clientId)
|
addStrToScopeMap(bs.StarSubs, scope, subRouteId)
|
||||||
} else {
|
} else {
|
||||||
addStrToScopeMap(bs.ScopeSubs, scope, clientId)
|
addStrToScopeMap(bs.ScopeSubs, scope, subRouteId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,9 +87,9 @@ func (bs *BrokerSubscription) IsEmpty() bool {
|
|||||||
return len(bs.AllSubs) == 0 && len(bs.ScopeSubs) == 0 && len(bs.StarSubs) == 0
|
return len(bs.AllSubs) == 0 && len(bs.ScopeSubs) == 0 && len(bs.StarSubs) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeStrFromScopeMap(scopeMap map[string][]string, scope string, clientId string) {
|
func removeStrFromScopeMap(scopeMap map[string][]string, scope string, routeId string) {
|
||||||
scopeSubs := scopeMap[scope]
|
scopeSubs := scopeMap[scope]
|
||||||
scopeSubs = utilfn.RemoveElemFromSlice(scopeSubs, clientId)
|
scopeSubs = utilfn.RemoveElemFromSlice(scopeSubs, routeId)
|
||||||
if len(scopeSubs) == 0 {
|
if len(scopeSubs) == 0 {
|
||||||
delete(scopeMap, scope)
|
delete(scopeMap, scope)
|
||||||
} else {
|
} else {
|
||||||
@ -88,9 +97,9 @@ func removeStrFromScopeMap(scopeMap map[string][]string, scope string, clientId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeStrFromScopeMapAll(scopeMap map[string][]string, clientId string) {
|
func removeStrFromScopeMapAll(scopeMap map[string][]string, routeId string) {
|
||||||
for scope, scopeSubs := range scopeMap {
|
for scope, scopeSubs := range scopeMap {
|
||||||
scopeSubs = utilfn.RemoveElemFromSlice(scopeSubs, clientId)
|
scopeSubs = utilfn.RemoveElemFromSlice(scopeSubs, routeId)
|
||||||
if len(scopeSubs) == 0 {
|
if len(scopeSubs) == 0 {
|
||||||
delete(scopeMap, scope)
|
delete(scopeMap, scope)
|
||||||
} else {
|
} else {
|
||||||
@ -99,29 +108,28 @@ func removeStrFromScopeMapAll(scopeMap map[string][]string, clientId string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addStrToScopeMap(scopeMap map[string][]string, scope string, clientId string) {
|
func addStrToScopeMap(scopeMap map[string][]string, scope string, routeId string) {
|
||||||
scopeSubs := scopeMap[scope]
|
scopeSubs := scopeMap[scope]
|
||||||
scopeSubs = utilfn.AddElemToSliceUniq(scopeSubs, clientId)
|
scopeSubs = utilfn.AddElemToSliceUniq(scopeSubs, routeId)
|
||||||
scopeMap[scope] = scopeSubs
|
scopeMap[scope] = scopeSubs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrokerType) Unsubscribe(subscriber Client, sub wshrpc.SubscriptionRequest) {
|
func (b *BrokerType) Unsubscribe(subRouteId string, sub wshrpc.SubscriptionRequest) {
|
||||||
b.Lock.Lock()
|
b.Lock.Lock()
|
||||||
defer b.Lock.Unlock()
|
defer b.Lock.Unlock()
|
||||||
clientId := subscriber.ClientId()
|
|
||||||
bs := b.SubMap[sub.Event]
|
bs := b.SubMap[sub.Event]
|
||||||
if bs == nil {
|
if bs == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sub.AllScopes {
|
if sub.AllScopes {
|
||||||
bs.AllSubs = utilfn.RemoveElemFromSlice(bs.AllSubs, clientId)
|
bs.AllSubs = utilfn.RemoveElemFromSlice(bs.AllSubs, subRouteId)
|
||||||
}
|
}
|
||||||
for _, scope := range sub.Scopes {
|
for _, scope := range sub.Scopes {
|
||||||
starMatch := scopeHasStarMatch(scope)
|
starMatch := scopeHasStarMatch(scope)
|
||||||
if starMatch {
|
if starMatch {
|
||||||
removeStrFromScopeMap(bs.StarSubs, scope, clientId)
|
removeStrFromScopeMap(bs.StarSubs, scope, subRouteId)
|
||||||
} else {
|
} else {
|
||||||
removeStrFromScopeMap(bs.ScopeSubs, scope, clientId)
|
removeStrFromScopeMap(bs.ScopeSubs, scope, subRouteId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bs.IsEmpty() {
|
if bs.IsEmpty() {
|
||||||
@ -129,15 +137,13 @@ func (b *BrokerType) Unsubscribe(subscriber Client, sub wshrpc.SubscriptionReque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrokerType) UnsubscribeAll(subscriber Client) {
|
func (b *BrokerType) UnsubscribeAll(subRouteId string) {
|
||||||
b.Lock.Lock()
|
b.Lock.Lock()
|
||||||
defer b.Lock.Unlock()
|
defer b.Lock.Unlock()
|
||||||
clientId := subscriber.ClientId()
|
|
||||||
delete(b.ClientMap, clientId)
|
|
||||||
for eventType, bs := range b.SubMap {
|
for eventType, bs := range b.SubMap {
|
||||||
bs.AllSubs = utilfn.RemoveElemFromSlice(bs.AllSubs, clientId)
|
bs.AllSubs = utilfn.RemoveElemFromSlice(bs.AllSubs, subRouteId)
|
||||||
removeStrFromScopeMapAll(bs.StarSubs, clientId)
|
removeStrFromScopeMapAll(bs.StarSubs, subRouteId)
|
||||||
removeStrFromScopeMapAll(bs.ScopeSubs, clientId)
|
removeStrFromScopeMapAll(bs.ScopeSubs, subRouteId)
|
||||||
if bs.IsEmpty() {
|
if bs.IsEmpty() {
|
||||||
delete(b.SubMap, eventType)
|
delete(b.SubMap, eventType)
|
||||||
}
|
}
|
||||||
@ -145,41 +151,42 @@ func (b *BrokerType) UnsubscribeAll(subscriber Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrokerType) Publish(event wshrpc.WaveEvent) {
|
func (b *BrokerType) Publish(event wshrpc.WaveEvent) {
|
||||||
clientIds := b.getMatchingClientIds(event)
|
client := b.GetClient()
|
||||||
for _, clientId := range clientIds {
|
if client == nil {
|
||||||
client := b.ClientMap[clientId]
|
return
|
||||||
if client != nil {
|
}
|
||||||
client.SendEvent(event)
|
routeIds := b.getMatchingRouteIds(event)
|
||||||
}
|
for _, routeId := range routeIds {
|
||||||
|
client.SendEvent(routeId, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrokerType) getMatchingClientIds(event wshrpc.WaveEvent) []string {
|
func (b *BrokerType) getMatchingRouteIds(event wshrpc.WaveEvent) []string {
|
||||||
b.Lock.Lock()
|
b.Lock.Lock()
|
||||||
defer b.Lock.Unlock()
|
defer b.Lock.Unlock()
|
||||||
bs := b.SubMap[event.Event]
|
bs := b.SubMap[event.Event]
|
||||||
if bs == nil {
|
if bs == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
clientIds := make(map[string]bool)
|
routeIds := make(map[string]bool)
|
||||||
for _, clientId := range bs.AllSubs {
|
for _, routeId := range bs.AllSubs {
|
||||||
clientIds[clientId] = true
|
routeIds[routeId] = true
|
||||||
}
|
}
|
||||||
for _, scope := range event.Scopes {
|
for _, scope := range event.Scopes {
|
||||||
for _, clientId := range bs.ScopeSubs[scope] {
|
for _, routeId := range bs.ScopeSubs[scope] {
|
||||||
clientIds[clientId] = true
|
routeIds[routeId] = true
|
||||||
}
|
}
|
||||||
for starScope := range bs.StarSubs {
|
for starScope := range bs.StarSubs {
|
||||||
if utilfn.StarMatchString(starScope, scope, ":") {
|
if utilfn.StarMatchString(starScope, scope, ":") {
|
||||||
for _, clientId := range bs.StarSubs[starScope] {
|
for _, routeId := range bs.StarSubs[starScope] {
|
||||||
clientIds[clientId] = true
|
routeIds[routeId] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rtn []string
|
var rtn []string
|
||||||
for clientId := range clientIds {
|
for routeId := range routeIds {
|
||||||
rtn = append(rtn, clientId)
|
rtn = append(rtn, routeId)
|
||||||
}
|
}
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
@ -12,147 +12,147 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// command "authenticate", wshserver.AuthenticateCommand
|
// command "authenticate", wshserver.AuthenticateCommand
|
||||||
func AuthenticateCommand(w *wshutil.WshRpc, data string, opts *wshrpc.WshRpcCommandOpts) error {
|
func AuthenticateCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "authenticate", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "authenticate", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "controllerinput", wshserver.ControllerInputCommand
|
// command "controllerinput", wshserver.ControllerInputCommand
|
||||||
func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.WshRpcCommandOpts) error {
|
func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "controllerrestart", wshserver.ControllerRestartCommand
|
// command "controllerrestart", wshserver.ControllerRestartCommand
|
||||||
func ControllerRestartCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockRestartData, opts *wshrpc.WshRpcCommandOpts) error {
|
func ControllerRestartCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockRestartData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "controllerrestart", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "controllerrestart", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "createblock", wshserver.CreateBlockCommand
|
// command "createblock", wshserver.CreateBlockCommand
|
||||||
func CreateBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandCreateBlockData, opts *wshrpc.WshRpcCommandOpts) (waveobj.ORef, error) {
|
func CreateBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandCreateBlockData, opts *wshrpc.RpcOpts) (waveobj.ORef, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[waveobj.ORef](w, "createblock", data, opts)
|
resp, err := sendRpcRequestCallHelper[waveobj.ORef](w, "createblock", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "deleteblock", wshserver.DeleteBlockCommand
|
// command "deleteblock", wshserver.DeleteBlockCommand
|
||||||
func DeleteBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandDeleteBlockData, opts *wshrpc.WshRpcCommandOpts) error {
|
func DeleteBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandDeleteBlockData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "deleteblock", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "deleteblock", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventpublish", wshserver.EventPublishCommand
|
// command "eventpublish", wshserver.EventPublishCommand
|
||||||
func EventPublishCommand(w *wshutil.WshRpc, data wshrpc.WaveEvent, opts *wshrpc.WshRpcCommandOpts) error {
|
func EventPublishCommand(w *wshutil.WshRpc, data wshrpc.WaveEvent, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "eventpublish", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "eventpublish", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventrecv", wshserver.EventRecvCommand
|
// command "eventrecv", wshserver.EventRecvCommand
|
||||||
func EventRecvCommand(w *wshutil.WshRpc, data wshrpc.WaveEvent, opts *wshrpc.WshRpcCommandOpts) error {
|
func EventRecvCommand(w *wshutil.WshRpc, data wshrpc.WaveEvent, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "eventrecv", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "eventrecv", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventsub", wshserver.EventSubCommand
|
// command "eventsub", wshserver.EventSubCommand
|
||||||
func EventSubCommand(w *wshutil.WshRpc, data wshrpc.SubscriptionRequest, opts *wshrpc.WshRpcCommandOpts) error {
|
func EventSubCommand(w *wshutil.WshRpc, data wshrpc.SubscriptionRequest, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "eventsub", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "eventsub", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventunsub", wshserver.EventUnsubCommand
|
// command "eventunsub", wshserver.EventUnsubCommand
|
||||||
func EventUnsubCommand(w *wshutil.WshRpc, data wshrpc.SubscriptionRequest, opts *wshrpc.WshRpcCommandOpts) error {
|
func EventUnsubCommand(w *wshutil.WshRpc, data wshrpc.SubscriptionRequest, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "eventunsub", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "eventunsub", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "eventunsuball", wshserver.EventUnsubAllCommand
|
// command "eventunsuball", wshserver.EventUnsubAllCommand
|
||||||
func EventUnsubAllCommand(w *wshutil.WshRpc, opts *wshrpc.WshRpcCommandOpts) error {
|
func EventUnsubAllCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "eventunsuball", nil, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "eventunsuball", nil, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileappend", wshserver.FileAppendCommand
|
// command "fileappend", wshserver.FileAppendCommand
|
||||||
func FileAppendCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.WshRpcCommandOpts) error {
|
func FileAppendCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "fileappend", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "fileappend", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileappendijson", wshserver.FileAppendIJsonCommand
|
// command "fileappendijson", wshserver.FileAppendIJsonCommand
|
||||||
func FileAppendIJsonCommand(w *wshutil.WshRpc, data wshrpc.CommandAppendIJsonData, opts *wshrpc.WshRpcCommandOpts) error {
|
func FileAppendIJsonCommand(w *wshutil.WshRpc, data wshrpc.CommandAppendIJsonData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "fileappendijson", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "fileappendijson", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "fileread", wshserver.FileReadCommand
|
// command "fileread", wshserver.FileReadCommand
|
||||||
func FileReadCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.WshRpcCommandOpts) (string, error) {
|
func FileReadCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.RpcOpts) (string, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[string](w, "fileread", data, opts)
|
resp, err := sendRpcRequestCallHelper[string](w, "fileread", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "filewrite", wshserver.FileWriteCommand
|
// command "filewrite", wshserver.FileWriteCommand
|
||||||
func FileWriteCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.WshRpcCommandOpts) error {
|
func FileWriteCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "filewrite", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "filewrite", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "getmeta", wshserver.GetMetaCommand
|
// command "getmeta", wshserver.GetMetaCommand
|
||||||
func GetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandGetMetaData, opts *wshrpc.WshRpcCommandOpts) (waveobj.MetaMapType, error) {
|
func GetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandGetMetaData, opts *wshrpc.RpcOpts) (waveobj.MetaMapType, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[waveobj.MetaMapType](w, "getmeta", data, opts)
|
resp, err := sendRpcRequestCallHelper[waveobj.MetaMapType](w, "getmeta", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "message", wshserver.MessageCommand
|
// command "message", wshserver.MessageCommand
|
||||||
func MessageCommand(w *wshutil.WshRpc, data wshrpc.CommandMessageData, opts *wshrpc.WshRpcCommandOpts) error {
|
func MessageCommand(w *wshutil.WshRpc, data wshrpc.CommandMessageData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "message", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "message", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "remotefileinfo", wshserver.RemoteFileInfoCommand
|
// command "remotefileinfo", wshserver.RemoteFileInfoCommand
|
||||||
func RemoteFileInfoCommand(w *wshutil.WshRpc, data string, opts *wshrpc.WshRpcCommandOpts) (*wshrpc.FileInfo, error) {
|
func RemoteFileInfoCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) (*wshrpc.FileInfo, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[*wshrpc.FileInfo](w, "remotefileinfo", data, opts)
|
resp, err := sendRpcRequestCallHelper[*wshrpc.FileInfo](w, "remotefileinfo", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "remotestreamfile", wshserver.RemoteStreamFileCommand
|
// command "remotestreamfile", wshserver.RemoteStreamFileCommand
|
||||||
func RemoteStreamFileCommand(w *wshutil.WshRpc, data wshrpc.CommandRemoteStreamFileData, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteStreamFileRtnData] {
|
func RemoteStreamFileCommand(w *wshutil.WshRpc, data wshrpc.CommandRemoteStreamFileData, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteStreamFileRtnData] {
|
||||||
return sendRpcRequestResponseStreamHelper[wshrpc.CommandRemoteStreamFileRtnData](w, "remotestreamfile", data, opts)
|
return sendRpcRequestResponseStreamHelper[wshrpc.CommandRemoteStreamFileRtnData](w, "remotestreamfile", data, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "resolveids", wshserver.ResolveIdsCommand
|
// command "resolveids", wshserver.ResolveIdsCommand
|
||||||
func ResolveIdsCommand(w *wshutil.WshRpc, data wshrpc.CommandResolveIdsData, opts *wshrpc.WshRpcCommandOpts) (wshrpc.CommandResolveIdsRtnData, error) {
|
func ResolveIdsCommand(w *wshutil.WshRpc, data wshrpc.CommandResolveIdsData, opts *wshrpc.RpcOpts) (wshrpc.CommandResolveIdsRtnData, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[wshrpc.CommandResolveIdsRtnData](w, "resolveids", data, opts)
|
resp, err := sendRpcRequestCallHelper[wshrpc.CommandResolveIdsRtnData](w, "resolveids", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "setmeta", wshserver.SetMetaCommand
|
// command "setmeta", wshserver.SetMetaCommand
|
||||||
func SetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandSetMetaData, opts *wshrpc.WshRpcCommandOpts) error {
|
func SetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandSetMetaData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "setmeta", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "setmeta", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "setview", wshserver.SetViewCommand
|
// command "setview", wshserver.SetViewCommand
|
||||||
func SetViewCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockSetViewData, opts *wshrpc.WshRpcCommandOpts) error {
|
func SetViewCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockSetViewData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "setview", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "setview", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamcpudata", wshserver.StreamCpuDataCommand
|
// command "streamcpudata", wshserver.StreamCpuDataCommand
|
||||||
func StreamCpuDataCommand(w *wshutil.WshRpc, data wshrpc.CpuDataRequest, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[wshrpc.CpuDataType] {
|
func StreamCpuDataCommand(w *wshutil.WshRpc, data wshrpc.CpuDataRequest, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[wshrpc.CpuDataType] {
|
||||||
return sendRpcRequestResponseStreamHelper[wshrpc.CpuDataType](w, "streamcpudata", data, opts)
|
return sendRpcRequestResponseStreamHelper[wshrpc.CpuDataType](w, "streamcpudata", data, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamtest", wshserver.StreamTestCommand
|
// command "streamtest", wshserver.StreamTestCommand
|
||||||
func StreamTestCommand(w *wshutil.WshRpc, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[int] {
|
func StreamTestCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[int] {
|
||||||
return sendRpcRequestResponseStreamHelper[int](w, "streamtest", nil, opts)
|
return sendRpcRequestResponseStreamHelper[int](w, "streamtest", nil, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "streamwaveai", wshserver.StreamWaveAiCommand
|
// command "streamwaveai", wshserver.StreamWaveAiCommand
|
||||||
func StreamWaveAiCommand(w *wshutil.WshRpc, data wshrpc.OpenAiStreamRequest, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType] {
|
func StreamWaveAiCommand(w *wshutil.WshRpc, data wshrpc.OpenAiStreamRequest, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType] {
|
||||||
return sendRpcRequestResponseStreamHelper[wshrpc.OpenAIPacketType](w, "streamwaveai", data, opts)
|
return sendRpcRequestResponseStreamHelper[wshrpc.OpenAIPacketType](w, "streamwaveai", data, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "test", wshserver.TestCommand
|
// command "test", wshserver.TestCommand
|
||||||
func TestCommand(w *wshutil.WshRpc, data string, opts *wshrpc.WshRpcCommandOpts) error {
|
func TestCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "test", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "test", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -9,19 +9,19 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sendRpcRequestCallHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.WshRpcCommandOpts) (T, error) {
|
func sendRpcRequestCallHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.RpcOpts) (T, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &wshrpc.WshRpcCommandOpts{}
|
opts = &wshrpc.RpcOpts{}
|
||||||
}
|
}
|
||||||
var respData T
|
var respData T
|
||||||
if opts.NoResponse {
|
if opts.NoResponse {
|
||||||
err := w.SendCommand(command, data)
|
err := w.SendCommand(command, data, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return respData, err
|
return respData, err
|
||||||
}
|
}
|
||||||
return respData, nil
|
return respData, nil
|
||||||
}
|
}
|
||||||
resp, err := w.SendRpcRequest(command, data, opts.Timeout)
|
resp, err := w.SendRpcRequest(command, data, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return respData, err
|
return respData, err
|
||||||
}
|
}
|
||||||
@ -32,12 +32,12 @@ func sendRpcRequestCallHelper[T any](w *wshutil.WshRpc, command string, data int
|
|||||||
return respData, nil
|
return respData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendRpcRequestResponseStreamHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.WshRpcCommandOpts) chan wshrpc.RespOrErrorUnion[T] {
|
func sendRpcRequestResponseStreamHelper[T any](w *wshutil.WshRpc, command string, data interface{}, opts *wshrpc.RpcOpts) chan wshrpc.RespOrErrorUnion[T] {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &wshrpc.WshRpcCommandOpts{}
|
opts = &wshrpc.RpcOpts{}
|
||||||
}
|
}
|
||||||
respChan := make(chan wshrpc.RespOrErrorUnion[T])
|
respChan := make(chan wshrpc.RespOrErrorUnion[T])
|
||||||
reqHandler, err := w.SendComplexRequest(command, data, true, opts.Timeout)
|
reqHandler, err := w.SendComplexRequest(command, data, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
go func() {
|
go func() {
|
||||||
respChan <- wshrpc.RespOrErrorUnion[T]{Error: err}
|
respChan <- wshrpc.RespOrErrorUnion[T]{Error: err}
|
||||||
|
@ -6,6 +6,7 @@ package wshrpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ type RespOrErrorUnion[T any] struct {
|
|||||||
|
|
||||||
type WshRpcInterface interface {
|
type WshRpcInterface interface {
|
||||||
AuthenticateCommand(ctx context.Context, data string) error
|
AuthenticateCommand(ctx context.Context, data string) error
|
||||||
|
|
||||||
MessageCommand(ctx context.Context, data CommandMessageData) error
|
MessageCommand(ctx context.Context, data CommandMessageData) error
|
||||||
GetMetaCommand(ctx context.Context, data CommandGetMetaData) (wstore.MetaMapType, error)
|
GetMetaCommand(ctx context.Context, data CommandGetMetaData) (wstore.MetaMapType, error)
|
||||||
SetMetaCommand(ctx context.Context, data CommandSetMetaData) error
|
SetMetaCommand(ctx context.Context, data CommandSetMetaData) error
|
||||||
@ -90,15 +92,15 @@ type WshServerCommandMeta struct {
|
|||||||
CommandType string `json:"commandtype"`
|
CommandType string `json:"commandtype"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WshRpcCommandOpts struct {
|
type RpcOpts struct {
|
||||||
Timeout int `json:"timeout"`
|
Timeout int `json:"timeout,omitempty"`
|
||||||
NoResponse bool `json:"noresponse"`
|
NoResponse bool `json:"noresponse,omitempty"`
|
||||||
|
Route string `json:"route,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RpcContext struct {
|
type RpcContext struct {
|
||||||
BlockId string `json:"blockid,omitempty"`
|
BlockId string `json:"blockid,omitempty"`
|
||||||
TabId string `json:"tabid,omitempty"`
|
TabId string `json:"tabid,omitempty"`
|
||||||
WindowId string `json:"windowid,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HackRpcContextIntoData(dataPtr any, rpcContext RpcContext) {
|
func HackRpcContextIntoData(dataPtr any, rpcContext RpcContext) {
|
||||||
@ -122,12 +124,12 @@ func HackRpcContextIntoData(dataPtr any, rpcContext RpcContext) {
|
|||||||
field.SetString(rpcContext.BlockId)
|
field.SetString(rpcContext.BlockId)
|
||||||
case "TabId":
|
case "TabId":
|
||||||
field.SetString(rpcContext.TabId)
|
field.SetString(rpcContext.TabId)
|
||||||
case "WindowId":
|
|
||||||
field.SetString(rpcContext.WindowId)
|
|
||||||
case "BlockORef":
|
case "BlockORef":
|
||||||
if rpcContext.BlockId != "" {
|
if rpcContext.BlockId != "" {
|
||||||
field.Set(reflect.ValueOf(waveobj.MakeORef(wstore.OType_Block, rpcContext.BlockId)))
|
field.Set(reflect.ValueOf(waveobj.MakeORef(wstore.OType_Block, rpcContext.BlockId)))
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("invalid wshcontext tag: %q in type(%T)", tag, dataPtr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +149,8 @@ type CommandSetMetaData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommandResolveIdsData struct {
|
type CommandResolveIdsData struct {
|
||||||
Ids []string `json:"ids"`
|
BlockId string `json:"blockid" wshcontext:"BlockId"`
|
||||||
|
Ids []string `json:"ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandResolveIdsRtnData struct {
|
type CommandResolveIdsRtnData struct {
|
||||||
|
@ -30,26 +30,33 @@ import (
|
|||||||
|
|
||||||
const SimpleId_This = "this"
|
const SimpleId_This = "this"
|
||||||
|
|
||||||
|
type WshServer struct{}
|
||||||
|
|
||||||
|
func (*WshServer) WshServerImpl() {}
|
||||||
|
|
||||||
|
var WshServerImpl = WshServer{}
|
||||||
|
|
||||||
func (ws *WshServer) TestCommand(ctx context.Context, data string) error {
|
func (ws *WshServer) TestCommand(ctx context.Context, data string) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
log.Printf("panic in TestCommand: %v", r)
|
log.Printf("panic in TestCommand: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
rpc := wshutil.GetWshRpcFromContext(ctx)
|
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||||
if rpc == nil {
|
log.Printf("TEST src:%s | %s\n", rpcSource, data)
|
||||||
|
if rpcSource == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
wshclient.MessageCommand(rpc, wshrpc.CommandMessageData{Message: "test message"}, &wshrpc.WshRpcCommandOpts{NoResponse: true})
|
mainClient := GetMainRpcClient()
|
||||||
resp, err := wshclient.RemoteFileInfoCommand(rpc, "~/work/wails/thenextwave/README.md", nil)
|
wshclient.MessageCommand(mainClient, wshrpc.CommandMessageData{Message: "test message"}, &wshrpc.RpcOpts{NoResponse: true, Route: rpcSource})
|
||||||
|
resp, err := wshclient.RemoteFileInfoCommand(mainClient, "~/work/wails/thenextwave/README.md", &wshrpc.RpcOpts{Route: rpcSource})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error getting remote file info: %v", err)
|
log.Printf("error getting remote file info: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("remote file info: %#v\n", resp)
|
log.Printf("remote file info: %#v\n", resp)
|
||||||
|
rch := wshclient.RemoteStreamFileCommand(mainClient, wshrpc.CommandRemoteStreamFileData{Path: "~/work/wails/thenextwave/README.md"}, &wshrpc.RpcOpts{Route: rpcSource})
|
||||||
rch := wshclient.RemoteStreamFileCommand(rpc, wshrpc.CommandRemoteStreamFileData{Path: "~/work/wails/thenextwave/README.md"}, nil)
|
|
||||||
for msg := range rch {
|
for msg := range rch {
|
||||||
if msg.Error != nil {
|
if msg.Error != nil {
|
||||||
log.Printf("error in stream: %v", msg.Error)
|
log.Printf("error in stream: %v", msg.Error)
|
||||||
@ -66,22 +73,6 @@ func (ws *WshServer) TestCommand(ctx context.Context, data string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) AuthenticateCommand(ctx context.Context, data string) error {
|
|
||||||
w := wshutil.GetWshRpcFromContext(ctx)
|
|
||||||
if w == nil {
|
|
||||||
return fmt.Errorf("no wshrpc in context")
|
|
||||||
}
|
|
||||||
newCtx, err := wshutil.ValidateAndExtractRpcContextFromToken(data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error validating token: %w", err)
|
|
||||||
}
|
|
||||||
if newCtx == nil {
|
|
||||||
return fmt.Errorf("no context found in jwt token")
|
|
||||||
}
|
|
||||||
w.SetRpcContext(*newCtx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// for testing
|
// for testing
|
||||||
func (ws *WshServer) MessageCommand(ctx context.Context, data wshrpc.CommandMessageData) error {
|
func (ws *WshServer) MessageCommand(ctx context.Context, data wshrpc.CommandMessageData) error {
|
||||||
log.Printf("MESSAGE: %s | %q\n", data.ORef, data.Message)
|
log.Printf("MESSAGE: %s | %q\n", data.ORef, data.Message)
|
||||||
@ -228,17 +219,12 @@ func sendWaveObjUpdate(oref waveobj.ORef) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSimpleId(ctx context.Context, simpleId string) (*waveobj.ORef, error) {
|
func resolveSimpleId(ctx context.Context, data wshrpc.CommandResolveIdsData, simpleId string) (*waveobj.ORef, error) {
|
||||||
if simpleId == SimpleId_This {
|
if simpleId == SimpleId_This {
|
||||||
wshRpc := wshutil.GetWshRpcFromContext(ctx)
|
if data.BlockId == "" {
|
||||||
if wshRpc == nil {
|
return nil, fmt.Errorf("no blockid in request")
|
||||||
return nil, fmt.Errorf("no wshrpc in context")
|
|
||||||
}
|
}
|
||||||
rpcCtx := wshRpc.GetRpcContext()
|
return &waveobj.ORef{OType: wstore.OType_Block, OID: data.BlockId}, nil
|
||||||
if rpcCtx.BlockId == "" {
|
|
||||||
return nil, fmt.Errorf("no blockid in rpc context")
|
|
||||||
}
|
|
||||||
return &waveobj.ORef{OType: wstore.OType_Block, OID: rpcCtx.BlockId}, nil
|
|
||||||
}
|
}
|
||||||
if strings.Contains(simpleId, ":") {
|
if strings.Contains(simpleId, ":") {
|
||||||
rtn, err := waveobj.ParseORef(simpleId)
|
rtn, err := waveobj.ParseORef(simpleId)
|
||||||
@ -254,7 +240,7 @@ func (ws *WshServer) ResolveIdsCommand(ctx context.Context, data wshrpc.CommandR
|
|||||||
rtn := wshrpc.CommandResolveIdsRtnData{}
|
rtn := wshrpc.CommandResolveIdsRtnData{}
|
||||||
rtn.ResolvedIds = make(map[string]waveobj.ORef)
|
rtn.ResolvedIds = make(map[string]waveobj.ORef)
|
||||||
for _, simpleId := range data.Ids {
|
for _, simpleId := range data.Ids {
|
||||||
oref, err := resolveSimpleId(ctx, simpleId)
|
oref, err := resolveSimpleId(ctx, data, simpleId)
|
||||||
if err != nil || oref == nil {
|
if err != nil || oref == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -471,40 +457,40 @@ func (ws *WshServer) EventRecvCommand(ctx context.Context, data wshrpc.WaveEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) EventPublishCommand(ctx context.Context, data wshrpc.WaveEvent) error {
|
func (ws *WshServer) EventPublishCommand(ctx context.Context, data wshrpc.WaveEvent) error {
|
||||||
wrpc := wshutil.GetWshRpcFromContext(ctx)
|
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||||
if wrpc == nil {
|
if rpcSource == "" {
|
||||||
return fmt.Errorf("no wshrpc in context")
|
return fmt.Errorf("no rpc source set")
|
||||||
}
|
}
|
||||||
if data.Sender == "" {
|
if data.Sender == "" {
|
||||||
data.Sender = wrpc.ClientId()
|
data.Sender = rpcSource
|
||||||
}
|
}
|
||||||
wps.Broker.Publish(data)
|
wps.Broker.Publish(data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) EventSubCommand(ctx context.Context, data wshrpc.SubscriptionRequest) error {
|
func (ws *WshServer) EventSubCommand(ctx context.Context, data wshrpc.SubscriptionRequest) error {
|
||||||
wrpc := wshutil.GetWshRpcFromContext(ctx)
|
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||||
if wrpc == nil {
|
if rpcSource == "" {
|
||||||
return fmt.Errorf("no wshrpc in context")
|
return fmt.Errorf("no rpc source set")
|
||||||
}
|
}
|
||||||
wps.Broker.Subscribe(wrpc, data)
|
wps.Broker.Subscribe(rpcSource, data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) EventUnsubCommand(ctx context.Context, data wshrpc.SubscriptionRequest) error {
|
func (ws *WshServer) EventUnsubCommand(ctx context.Context, data wshrpc.SubscriptionRequest) error {
|
||||||
wrpc := wshutil.GetWshRpcFromContext(ctx)
|
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||||
if wrpc == nil {
|
if rpcSource == "" {
|
||||||
return fmt.Errorf("no wshrpc in context")
|
return fmt.Errorf("no rpc source set")
|
||||||
}
|
}
|
||||||
wps.Broker.Unsubscribe(wrpc, data)
|
wps.Broker.Unsubscribe(rpcSource, data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) EventUnsubAllCommand(ctx context.Context) error {
|
func (ws *WshServer) EventUnsubAllCommand(ctx context.Context) error {
|
||||||
wrpc := wshutil.GetWshRpcFromContext(ctx)
|
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||||
if wrpc == nil {
|
if rpcSource == "" {
|
||||||
return fmt.Errorf("no wshrpc in context")
|
return fmt.Errorf("no rpc source set")
|
||||||
}
|
}
|
||||||
wps.Broker.UnsubscribeAll(wrpc)
|
wps.Broker.UnsubscribeAll(rpcSource)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -4,89 +4,42 @@
|
|||||||
package wshserver
|
package wshserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"sync"
|
||||||
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this file contains the generic types and functions that create and power the WSH server
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultOutputChSize = 32
|
DefaultOutputChSize = 32
|
||||||
DefaultInputChSize = 32
|
DefaultInputChSize = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
type WshServer struct{}
|
func handleDomainSocketClient(conn net.Conn) {
|
||||||
|
proxy := wshutil.MakeRpcProxy()
|
||||||
func (*WshServer) WshServerImpl() {}
|
go func() {
|
||||||
|
writeErr := wshutil.AdaptOutputChToStream(proxy.ToRemoteCh, conn)
|
||||||
type WshServerMethodDecl struct {
|
if writeErr != nil {
|
||||||
Command string
|
log.Printf("error writing to domain socket: %v\n", writeErr)
|
||||||
CommandType string
|
|
||||||
MethodName string
|
|
||||||
Method reflect.Value
|
|
||||||
CommandDataType reflect.Type
|
|
||||||
DefaultResponseDataType reflect.Type
|
|
||||||
RequestDataTypes []reflect.Type // for streaming requests
|
|
||||||
ResponseDataTypes []reflect.Type // for streaming responses
|
|
||||||
}
|
|
||||||
|
|
||||||
var WshServerImpl = WshServer{}
|
|
||||||
var contextRType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
|
||||||
var wshCommandDeclMap = wshrpc.GenerateWshCommandDeclMap()
|
|
||||||
|
|
||||||
func GetWshServerMethod(command string, commandType string, methodName string, methodFunc any) *WshServerMethodDecl {
|
|
||||||
methodVal := reflect.ValueOf(methodFunc)
|
|
||||||
methodType := methodVal.Type()
|
|
||||||
if methodType.Kind() != reflect.Func {
|
|
||||||
panic(fmt.Sprintf("methodVal must be a function got [%v]", methodType))
|
|
||||||
}
|
|
||||||
if methodType.In(0) != contextRType {
|
|
||||||
panic(fmt.Sprintf("methodVal must have a context as the first argument %v", methodType))
|
|
||||||
}
|
|
||||||
var defResponseType reflect.Type
|
|
||||||
if methodType.NumOut() > 1 {
|
|
||||||
defResponseType = methodType.Out(0)
|
|
||||||
}
|
|
||||||
var cdataType reflect.Type
|
|
||||||
if methodType.NumIn() > 1 {
|
|
||||||
cdataType = methodType.In(1)
|
|
||||||
}
|
|
||||||
rtn := &WshServerMethodDecl{
|
|
||||||
Command: command,
|
|
||||||
CommandType: commandType,
|
|
||||||
MethodName: methodName,
|
|
||||||
Method: methodVal,
|
|
||||||
CommandDataType: cdataType,
|
|
||||||
DefaultResponseDataType: defResponseType,
|
|
||||||
}
|
|
||||||
return rtn
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeRtnVals(rtnVals []reflect.Value) (any, error) {
|
|
||||||
switch len(rtnVals) {
|
|
||||||
case 0:
|
|
||||||
return nil, nil
|
|
||||||
case 1:
|
|
||||||
errIf := rtnVals[0].Interface()
|
|
||||||
if errIf == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
return nil, errIf.(error)
|
}()
|
||||||
case 2:
|
go func() {
|
||||||
errIf := rtnVals[1].Interface()
|
// when input is closed, close the connection
|
||||||
if errIf == nil {
|
defer conn.Close()
|
||||||
return rtnVals[0].Interface(), nil
|
wshutil.AdaptStreamToMsgCh(conn, proxy.FromRemoteCh)
|
||||||
}
|
}()
|
||||||
return rtnVals[0].Interface(), errIf.(error)
|
rpcCtx, err := proxy.HandleAuthentication()
|
||||||
default:
|
if err != nil {
|
||||||
return nil, fmt.Errorf("too many return values: %d", len(rtnVals))
|
conn.Close()
|
||||||
|
log.Printf("error handling authentication: %v\n", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
// now that we're authenticated, set the ctx and attach to the router
|
||||||
|
log.Printf("domain socket connection authenticated: %#v\n", rpcCtx)
|
||||||
|
proxy.SetRpcContext(rpcCtx)
|
||||||
|
wshutil.DefaultRouter.RegisterRoute("controller:"+rpcCtx.BlockId, proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunWshRpcOverListener(listener net.Listener) {
|
func RunWshRpcOverListener(listener net.Listener) {
|
||||||
@ -98,11 +51,19 @@ func RunWshRpcOverListener(listener net.Listener) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Print("got domain socket connection\n")
|
log.Print("got domain socket connection\n")
|
||||||
// TODO deal with closing connection
|
go handleDomainSocketClient(conn)
|
||||||
go wshutil.SetupConnRpcClient(conn, &WshServerImpl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeWshServer(inputCh chan []byte, outputCh chan []byte, initialCtx wshrpc.RpcContext) {
|
var waveSrvClient_Singleton *wshutil.WshRpc
|
||||||
wshutil.MakeWshRpc(inputCh, outputCh, initialCtx, &WshServerImpl)
|
var waveSrvClient_Once = &sync.Once{}
|
||||||
|
|
||||||
|
// returns the wavesrv main rpc client singleton
|
||||||
|
func GetMainRpcClient() *wshutil.WshRpc {
|
||||||
|
waveSrvClient_Once.Do(func() {
|
||||||
|
inputCh := make(chan []byte, DefaultInputChSize)
|
||||||
|
outputCh := make(chan []byte, DefaultOutputChSize)
|
||||||
|
waveSrvClient_Singleton = wshutil.MakeWshRpc(inputCh, outputCh, wshrpc.RpcContext{}, &WshServerImpl)
|
||||||
|
})
|
||||||
|
return waveSrvClient_Singleton
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,31 @@ func noImplHandler(handler *RpcResponseHandler) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func recodeCommandData(command string, data any, rpcCtx *wshrpc.RpcContext) (any, error) {
|
||||||
|
// only applies to initial command packet
|
||||||
|
if command == "" {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
methodDecl := WshCommandDeclMap[command]
|
||||||
|
if methodDecl == nil {
|
||||||
|
return data, fmt.Errorf("command %q not found", command)
|
||||||
|
}
|
||||||
|
if methodDecl.CommandDataType == nil {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
commandDataPtr := reflect.New(methodDecl.CommandDataType).Interface()
|
||||||
|
if data != nil {
|
||||||
|
err := utilfn.ReUnmarshal(commandDataPtr, data)
|
||||||
|
if err != nil {
|
||||||
|
return data, fmt.Errorf("error re-marshalling command data: %w", err)
|
||||||
|
}
|
||||||
|
if rpcCtx != nil {
|
||||||
|
wshrpc.HackRpcContextIntoData(commandDataPtr, *rpcCtx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(commandDataPtr).Elem().Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func serverImplAdapter(impl any) func(*RpcResponseHandler) bool {
|
func serverImplAdapter(impl any) func(*RpcResponseHandler) bool {
|
||||||
if impl == nil {
|
if impl == nil {
|
||||||
return noImplHandler
|
return noImplHandler
|
||||||
@ -81,14 +106,13 @@ func serverImplAdapter(impl any) func(*RpcResponseHandler) bool {
|
|||||||
var callParams []reflect.Value
|
var callParams []reflect.Value
|
||||||
callParams = append(callParams, reflect.ValueOf(handler.Context()))
|
callParams = append(callParams, reflect.ValueOf(handler.Context()))
|
||||||
if methodDecl.CommandDataType != nil {
|
if methodDecl.CommandDataType != nil {
|
||||||
commandData := reflect.New(methodDecl.CommandDataType).Interface()
|
rpcCtx := handler.GetRpcContext()
|
||||||
err := utilfn.ReUnmarshal(commandData, handler.GetCommandRawData())
|
cmdData, err := recodeCommandData(cmd, handler.GetCommandRawData(), &rpcCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.SendResponseError(fmt.Errorf("error re-marshalling command data: %w", err))
|
handler.SendResponseError(err)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
wshrpc.HackRpcContextIntoData(commandData, handler.GetRpcContext())
|
callParams = append(callParams, reflect.ValueOf(cmdData))
|
||||||
callParams = append(callParams, reflect.ValueOf(commandData).Elem())
|
|
||||||
}
|
}
|
||||||
if methodDecl.CommandType == wshrpc.RpcType_Call {
|
if methodDecl.CommandType == wshrpc.RpcType_Call {
|
||||||
rtnVals := implMethod.Call(callParams)
|
rtnVals := implMethod.Call(callParams)
|
||||||
|
151
pkg/wshutil/wshproxy.go
Normal file
151
pkg/wshutil/wshproxy.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wshutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WshRpcProxy struct {
|
||||||
|
Lock *sync.Mutex
|
||||||
|
RpcContext *wshrpc.RpcContext
|
||||||
|
ToRemoteCh chan []byte
|
||||||
|
FromRemoteCh chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeRpcProxy() *WshRpcProxy {
|
||||||
|
return &WshRpcProxy{
|
||||||
|
Lock: &sync.Mutex{},
|
||||||
|
ToRemoteCh: make(chan []byte, DefaultInputChSize),
|
||||||
|
FromRemoteCh: make(chan []byte, DefaultOutputChSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) SetRpcContext(rpcCtx *wshrpc.RpcContext) {
|
||||||
|
p.Lock.Lock()
|
||||||
|
defer p.Lock.Unlock()
|
||||||
|
p.RpcContext = rpcCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) GetRpcContext() *wshrpc.RpcContext {
|
||||||
|
p.Lock.Lock()
|
||||||
|
defer p.Lock.Unlock()
|
||||||
|
return p.RpcContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) sendResponseError(msg RpcMessage, sendErr error) {
|
||||||
|
if msg.ReqId == "" {
|
||||||
|
// no response needed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := RpcMessage{
|
||||||
|
ResId: msg.ReqId,
|
||||||
|
Route: msg.Source,
|
||||||
|
Error: sendErr.Error(),
|
||||||
|
}
|
||||||
|
respBytes, _ := json.Marshal(resp)
|
||||||
|
p.SendRpcMessage(respBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) sendResponse(msg RpcMessage) {
|
||||||
|
if msg.ReqId == "" {
|
||||||
|
// no response needed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := RpcMessage{
|
||||||
|
ResId: msg.ReqId,
|
||||||
|
Route: msg.Source,
|
||||||
|
}
|
||||||
|
respBytes, _ := json.Marshal(resp)
|
||||||
|
p.SendRpcMessage(respBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAuthenticationCommand(msg RpcMessage) (*wshrpc.RpcContext, error) {
|
||||||
|
if msg.Data == nil {
|
||||||
|
return nil, fmt.Errorf("no data in authenticate message")
|
||||||
|
}
|
||||||
|
strData, ok := msg.Data.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("data in authenticate message not a string")
|
||||||
|
}
|
||||||
|
newCtx, err := ValidateAndExtractRpcContextFromToken(strData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error validating token: %w", err)
|
||||||
|
}
|
||||||
|
if newCtx == nil {
|
||||||
|
return nil, fmt.Errorf("no context found in jwt token")
|
||||||
|
}
|
||||||
|
if newCtx.BlockId == "" {
|
||||||
|
return nil, fmt.Errorf("no blockId found in jwt token")
|
||||||
|
}
|
||||||
|
if _, err := uuid.Parse(newCtx.BlockId); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid blockId in jwt token")
|
||||||
|
}
|
||||||
|
return newCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) HandleAuthentication() (*wshrpc.RpcContext, error) {
|
||||||
|
for {
|
||||||
|
msgBytes, ok := <-p.FromRemoteCh
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("remote closed, not authenticated")
|
||||||
|
}
|
||||||
|
var msg RpcMessage
|
||||||
|
err := json.Unmarshal(msgBytes, &msg)
|
||||||
|
if err != nil {
|
||||||
|
// nothing to do, can't even send a response since we don't have Source or ReqId
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if msg.Command == "" {
|
||||||
|
// this message is not allowed (protocol error at this point), ignore
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// we only allow one command "authenticate", everything else returns an error
|
||||||
|
if msg.Command != wshrpc.Command_Authenticate {
|
||||||
|
respErr := fmt.Errorf("connection not authenticated")
|
||||||
|
p.sendResponseError(msg, respErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCtx, err := handleAuthenticationCommand(msg)
|
||||||
|
if err != nil {
|
||||||
|
p.sendResponseError(msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.sendResponse(msg)
|
||||||
|
return newCtx, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) SendRpcMessage(msg []byte) {
|
||||||
|
p.ToRemoteCh <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WshRpcProxy) RecvRpcMessage() ([]byte, bool) {
|
||||||
|
msgBytes, ok := <-p.FromRemoteCh
|
||||||
|
if !ok || p.RpcContext == nil {
|
||||||
|
return msgBytes, ok
|
||||||
|
}
|
||||||
|
var msg RpcMessage
|
||||||
|
err := json.Unmarshal(msgBytes, &msg)
|
||||||
|
if err != nil {
|
||||||
|
// nothing to do here -- will error out at another level
|
||||||
|
return msgBytes, true
|
||||||
|
}
|
||||||
|
msg.Data, err = recodeCommandData(msg.Command, msg.Data, p.RpcContext)
|
||||||
|
if err != nil {
|
||||||
|
// nothing to do here -- will error out at another level
|
||||||
|
return msgBytes, true
|
||||||
|
}
|
||||||
|
newBytes, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
// nothing to do here
|
||||||
|
return msgBytes, true
|
||||||
|
}
|
||||||
|
return newBytes, true
|
||||||
|
}
|
212
pkg/wshutil/wshrouter.go
Normal file
212
pkg/wshutil/wshrouter.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wshutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type routeInfo struct {
|
||||||
|
RpcId string
|
||||||
|
SourceRouteId string
|
||||||
|
DestRouteId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WshRouter struct {
|
||||||
|
Lock *sync.Mutex
|
||||||
|
DefaultRoute string
|
||||||
|
RouteMap map[string]AbstractRpcClient
|
||||||
|
RpcMap map[string]*routeInfo
|
||||||
|
InputCh chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultRouter = NewWshRouter()
|
||||||
|
|
||||||
|
func NewWshRouter() *WshRouter {
|
||||||
|
rtn := &WshRouter{
|
||||||
|
Lock: &sync.Mutex{},
|
||||||
|
RouteMap: make(map[string]AbstractRpcClient),
|
||||||
|
RpcMap: make(map[string]*routeInfo),
|
||||||
|
InputCh: make(chan []byte, DefaultInputChSize),
|
||||||
|
}
|
||||||
|
go rtn.runServer()
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
|
||||||
|
func noRouteErr(routeId string) error {
|
||||||
|
if routeId == "" {
|
||||||
|
return errors.New("no default route")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("no route for %q", routeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) handleNoRoute(msg RpcMessage) {
|
||||||
|
nrErr := noRouteErr(msg.Route)
|
||||||
|
if msg.ReqId == "" {
|
||||||
|
if msg.Command == wshrpc.Command_Message {
|
||||||
|
// to prevent infinite loops
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// no response needed, but send message back to source
|
||||||
|
respMsg := RpcMessage{Command: wshrpc.Command_Message, Route: msg.Source, Data: wshrpc.CommandMessageData{Message: nrErr.Error()}}
|
||||||
|
respBytes, _ := json.Marshal(respMsg)
|
||||||
|
router.InputCh <- respBytes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// send error response
|
||||||
|
response := RpcMessage{
|
||||||
|
Route: msg.Source,
|
||||||
|
ResId: msg.ReqId,
|
||||||
|
Error: nrErr.Error(),
|
||||||
|
}
|
||||||
|
respBytes, _ := json.Marshal(response)
|
||||||
|
router.InputCh <- respBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) registerRouteInfo(rpcId string, sourceRouteId string, destRouteId string) {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
router.RpcMap[rpcId] = &routeInfo{RpcId: rpcId, SourceRouteId: sourceRouteId, DestRouteId: destRouteId}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) unregisterRouteInfo(rpcId string) {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
delete(router.RpcMap, rpcId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) getRouteInfo(rpcId string) *routeInfo {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
return router.RpcMap[rpcId]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) runServer() {
|
||||||
|
for msgBytes := range router.InputCh {
|
||||||
|
var msg RpcMessage
|
||||||
|
err := json.Unmarshal(msgBytes, &msg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error unmarshalling message: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var routeId string
|
||||||
|
msg.Route, routeId = popRoute(msg.Route)
|
||||||
|
if msg.Command != "" {
|
||||||
|
// new comand, setup new rpc
|
||||||
|
rpc := router.GetRpc(routeId)
|
||||||
|
if rpc == nil {
|
||||||
|
router.handleNoRoute(msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if msg.ReqId != "" {
|
||||||
|
router.registerRouteInfo(msg.ReqId, msg.Source, routeId)
|
||||||
|
}
|
||||||
|
rpc.SendRpcMessage(msgBytes)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// look at reqid or resid to route correctly
|
||||||
|
if msg.ReqId != "" {
|
||||||
|
routeInfo := router.getRouteInfo(msg.ReqId)
|
||||||
|
if routeInfo == nil {
|
||||||
|
// no route info, nothing to do
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rpc := router.GetRpc(routeInfo.DestRouteId)
|
||||||
|
if rpc != nil {
|
||||||
|
rpc.SendRpcMessage(msgBytes)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if msg.ResId != "" {
|
||||||
|
routeInfo := router.getRouteInfo(msg.ResId)
|
||||||
|
if routeInfo == nil {
|
||||||
|
// no route info, nothing to do
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rpc := router.GetRpc(routeInfo.SourceRouteId)
|
||||||
|
if rpc != nil {
|
||||||
|
rpc.SendRpcMessage(msgBytes)
|
||||||
|
}
|
||||||
|
if !msg.Cont {
|
||||||
|
router.unregisterRouteInfo(msg.ResId)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// this is a bad message (no command, reqid, or resid)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRoute(curRoute string, newRoute string) string {
|
||||||
|
if curRoute == "" {
|
||||||
|
return newRoute
|
||||||
|
}
|
||||||
|
return curRoute + "," + newRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns (newRoute, poppedRoute)
|
||||||
|
func popRoute(curRoute string) (string, string) {
|
||||||
|
routes := strings.Split(curRoute, ",")
|
||||||
|
if len(routes) == 1 {
|
||||||
|
return "", curRoute
|
||||||
|
}
|
||||||
|
return strings.Join(routes[:len(routes)-1], ","), routes[len(routes)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will also consume the output channel of the abstract client
|
||||||
|
func (router *WshRouter) RegisterRoute(routeId string, rpc AbstractRpcClient) {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
router.RouteMap[routeId] = rpc
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
msgBytes, ok := rpc.RecvRpcMessage()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var rpcMsg RpcMessage
|
||||||
|
err := json.Unmarshal(msgBytes, &rpcMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rpcMsg.Command != "" {
|
||||||
|
// new command, add source (for backward routing)
|
||||||
|
rpcMsg.Source = addRoute(rpcMsg.Source, routeId)
|
||||||
|
msgBytes, err = json.Marshal(rpcMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router.InputCh <- msgBytes
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) UnregisterRoute(routeId string) {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
delete(router.RouteMap, routeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *WshRouter) SetDefaultRoute(routeId string) {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
router.DefaultRoute = routeId
|
||||||
|
}
|
||||||
|
|
||||||
|
// this may return nil (returns default only for empty routeId)
|
||||||
|
func (router *WshRouter) GetRpc(routeId string) AbstractRpcClient {
|
||||||
|
router.Lock.Lock()
|
||||||
|
defer router.Lock.Unlock()
|
||||||
|
if routeId == "" {
|
||||||
|
routeId = router.DefaultRoute
|
||||||
|
}
|
||||||
|
return router.RouteMap[routeId]
|
||||||
|
}
|
@ -32,6 +32,11 @@ type ServerImpl interface {
|
|||||||
WshServerImpl()
|
WshServerImpl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AbstractRpcClient interface {
|
||||||
|
SendRpcMessage(msg []byte)
|
||||||
|
RecvRpcMessage() ([]byte, bool) // blocking
|
||||||
|
}
|
||||||
|
|
||||||
type WshRpc struct {
|
type WshRpc struct {
|
||||||
Lock *sync.Mutex
|
Lock *sync.Mutex
|
||||||
clientId string
|
clientId string
|
||||||
@ -45,11 +50,16 @@ type WshRpc struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type wshRpcContextKey struct{}
|
type wshRpcContextKey struct{}
|
||||||
|
type wshRpcRespHandlerContextKey struct{}
|
||||||
|
|
||||||
func withWshRpcContext(ctx context.Context, wshRpc *WshRpc) context.Context {
|
func withWshRpcContext(ctx context.Context, wshRpc *WshRpc) context.Context {
|
||||||
return context.WithValue(ctx, wshRpcContextKey{}, wshRpc)
|
return context.WithValue(ctx, wshRpcContextKey{}, wshRpc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withRespHandler(ctx context.Context, handler *RpcResponseHandler) context.Context {
|
||||||
|
return context.WithValue(ctx, wshRpcRespHandlerContextKey{}, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func GetWshRpcFromContext(ctx context.Context) *WshRpc {
|
func GetWshRpcFromContext(ctx context.Context) *WshRpc {
|
||||||
rtn := ctx.Value(wshRpcContextKey{})
|
rtn := ctx.Value(wshRpcContextKey{})
|
||||||
if rtn == nil {
|
if rtn == nil {
|
||||||
@ -58,11 +68,38 @@ func GetWshRpcFromContext(ctx context.Context) *WshRpc {
|
|||||||
return rtn.(*WshRpc)
|
return rtn.(*WshRpc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetRpcSourceFromContext(ctx context.Context) string {
|
||||||
|
rtn := ctx.Value(wshRpcRespHandlerContextKey{})
|
||||||
|
if rtn == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return rtn.(*RpcResponseHandler).GetSource()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRpcResponseHandlerFromContext(ctx context.Context) *RpcResponseHandler {
|
||||||
|
rtn := ctx.Value(wshRpcRespHandlerContextKey{})
|
||||||
|
if rtn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return rtn.(*RpcResponseHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WshRpc) SendRpcMessage(msg []byte) {
|
||||||
|
w.InputCh <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WshRpc) RecvRpcMessage() ([]byte, bool) {
|
||||||
|
msg, more := <-w.OutputCh
|
||||||
|
return msg, more
|
||||||
|
}
|
||||||
|
|
||||||
type RpcMessage struct {
|
type RpcMessage struct {
|
||||||
Command string `json:"command,omitempty"`
|
Command string `json:"command,omitempty"`
|
||||||
ReqId string `json:"reqid,omitempty"`
|
ReqId string `json:"reqid,omitempty"`
|
||||||
ResId string `json:"resid,omitempty"`
|
ResId string `json:"resid,omitempty"`
|
||||||
Timeout int `json:"timeout,omitempty"`
|
Timeout int `json:"timeout,omitempty"`
|
||||||
|
Route string `json:"route,omitempty"` // to route/forward requests to alternate servers
|
||||||
|
Source string `json:"source,omitempty"` // source route id
|
||||||
Cont bool `json:"cont,omitempty"` // flag if additional requests/responses are forthcoming
|
Cont bool `json:"cont,omitempty"` // flag if additional requests/responses are forthcoming
|
||||||
Cancel bool `json:"cancel,omitempty"` // used to cancel a streaming request or response (sent from the side that is not streaming)
|
Cancel bool `json:"cancel,omitempty"` // used to cancel a streaming request or response (sent from the side that is not streaming)
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
@ -141,7 +178,6 @@ func validateServerImpl(serverImpl ServerImpl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// oscEsc is the OSC escape sequence to use for *sending* messages
|
|
||||||
// closes outputCh when inputCh is closed/done
|
// closes outputCh when inputCh is closed/done
|
||||||
func MakeWshRpc(inputCh chan []byte, outputCh chan []byte, rpcCtx wshrpc.RpcContext, serverImpl ServerImpl) *WshRpc {
|
func MakeWshRpc(inputCh chan []byte, outputCh chan []byte, rpcCtx wshrpc.RpcContext, serverImpl ServerImpl) *WshRpc {
|
||||||
validateServerImpl(serverImpl)
|
validateServerImpl(serverImpl)
|
||||||
@ -235,12 +271,14 @@ func (w *WshRpc) handleRequest(req *RpcMessage) {
|
|||||||
reqId: req.ReqId,
|
reqId: req.ReqId,
|
||||||
command: req.Command,
|
command: req.Command,
|
||||||
commandData: req.Data,
|
commandData: req.Data,
|
||||||
|
source: req.Source,
|
||||||
done: &atomic.Bool{},
|
done: &atomic.Bool{},
|
||||||
canceled: &atomic.Bool{},
|
canceled: &atomic.Bool{},
|
||||||
contextCancelFn: &atomic.Pointer[context.CancelFunc]{},
|
contextCancelFn: &atomic.Pointer[context.CancelFunc]{},
|
||||||
rpcCtx: w.GetRpcContext(),
|
rpcCtx: w.GetRpcContext(),
|
||||||
}
|
}
|
||||||
respHandler.contextCancelFn.Store(&cancelFn)
|
respHandler.contextCancelFn.Store(&cancelFn)
|
||||||
|
respHandler.ctx = withRespHandler(ctx, respHandler)
|
||||||
w.registerResponseHandler(req.ReqId, respHandler)
|
w.registerResponseHandler(req.ReqId, respHandler)
|
||||||
isAsync := false
|
isAsync := false
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -347,8 +385,14 @@ func (w *WshRpc) unregisterRpc(reqId string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no response
|
// no response
|
||||||
func (w *WshRpc) SendCommand(command string, data any) error {
|
func (w *WshRpc) SendCommand(command string, data any, opts *wshrpc.RpcOpts) error {
|
||||||
handler, err := w.SendComplexRequest(command, data, false, 0)
|
var optsCopy wshrpc.RpcOpts
|
||||||
|
if opts != nil {
|
||||||
|
optsCopy = *opts
|
||||||
|
}
|
||||||
|
optsCopy.NoResponse = true
|
||||||
|
optsCopy.Timeout = 0
|
||||||
|
handler, err := w.SendComplexRequest(command, data, &optsCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -357,8 +401,13 @@ func (w *WshRpc) SendCommand(command string, data any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// single response
|
// single response
|
||||||
func (w *WshRpc) SendRpcRequest(command string, data any, timeoutMs int) (any, error) {
|
func (w *WshRpc) SendRpcRequest(command string, data any, opts *wshrpc.RpcOpts) (any, error) {
|
||||||
handler, err := w.SendComplexRequest(command, data, true, timeoutMs)
|
var optsCopy wshrpc.RpcOpts
|
||||||
|
if opts != nil {
|
||||||
|
optsCopy = *opts
|
||||||
|
}
|
||||||
|
optsCopy.NoResponse = false
|
||||||
|
handler, err := w.SendComplexRequest(command, data, &optsCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -444,6 +493,7 @@ type RpcResponseHandler struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
contextCancelFn *atomic.Pointer[context.CancelFunc]
|
contextCancelFn *atomic.Pointer[context.CancelFunc]
|
||||||
reqId string
|
reqId string
|
||||||
|
source string
|
||||||
command string
|
command string
|
||||||
commandData any
|
commandData any
|
||||||
rpcCtx wshrpc.RpcContext
|
rpcCtx wshrpc.RpcContext
|
||||||
@ -467,6 +517,10 @@ func (handler *RpcResponseHandler) GetRpcContext() wshrpc.RpcContext {
|
|||||||
return handler.rpcCtx
|
return handler.rpcCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (handler *RpcResponseHandler) GetSource() string {
|
||||||
|
return handler.source
|
||||||
|
}
|
||||||
|
|
||||||
func (handler *RpcResponseHandler) NeedsResponse() bool {
|
func (handler *RpcResponseHandler) NeedsResponse() bool {
|
||||||
return handler.reqId != ""
|
return handler.reqId != ""
|
||||||
}
|
}
|
||||||
@ -559,7 +613,11 @@ func (handler *RpcResponseHandler) IsDone() bool {
|
|||||||
return handler.done.Load()
|
return handler.done.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WshRpc) SendComplexRequest(command string, data any, expectsResponse bool, timeoutMs int) (rtnHandler *RpcRequestHandler, rtnErr error) {
|
func (w *WshRpc) SendComplexRequest(command string, data any, opts *wshrpc.RpcOpts) (rtnHandler *RpcRequestHandler, rtnErr error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &wshrpc.RpcOpts{}
|
||||||
|
}
|
||||||
|
timeoutMs := opts.Timeout
|
||||||
if timeoutMs <= 0 {
|
if timeoutMs <= 0 {
|
||||||
timeoutMs = DefaultTimeoutMs
|
timeoutMs = DefaultTimeoutMs
|
||||||
}
|
}
|
||||||
@ -579,7 +637,7 @@ func (w *WshRpc) SendComplexRequest(command string, data any, expectsResponse bo
|
|||||||
var cancelFn context.CancelFunc
|
var cancelFn context.CancelFunc
|
||||||
handler.ctx, cancelFn = context.WithTimeout(context.Background(), time.Duration(timeoutMs)*time.Millisecond)
|
handler.ctx, cancelFn = context.WithTimeout(context.Background(), time.Duration(timeoutMs)*time.Millisecond)
|
||||||
handler.ctxCancelFn.Store(&cancelFn)
|
handler.ctxCancelFn.Store(&cancelFn)
|
||||||
if expectsResponse {
|
if !opts.NoResponse {
|
||||||
handler.reqId = uuid.New().String()
|
handler.reqId = uuid.New().String()
|
||||||
}
|
}
|
||||||
req := &RpcMessage{
|
req := &RpcMessage{
|
||||||
@ -587,6 +645,7 @@ func (w *WshRpc) SendComplexRequest(command string, data any, expectsResponse bo
|
|||||||
ReqId: handler.reqId,
|
ReqId: handler.reqId,
|
||||||
Data: data,
|
Data: data,
|
||||||
Timeout: timeoutMs,
|
Timeout: timeoutMs,
|
||||||
|
Route: opts.Route,
|
||||||
}
|
}
|
||||||
barr, err := json.Marshal(req)
|
barr, err := json.Marshal(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -248,9 +248,6 @@ func MakeClientJWTToken(rpcCtx wshrpc.RpcContext, sockName string) (string, erro
|
|||||||
if rpcCtx.TabId != "" {
|
if rpcCtx.TabId != "" {
|
||||||
claims["tabid"] = rpcCtx.TabId
|
claims["tabid"] = rpcCtx.TabId
|
||||||
}
|
}
|
||||||
if rpcCtx.WindowId != "" {
|
|
||||||
claims["windowid"] = rpcCtx.WindowId
|
|
||||||
}
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
tokenStr, err := token.SignedString([]byte(wavebase.JwtSecret))
|
tokenStr, err := token.SignedString([]byte(wavebase.JwtSecret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -302,11 +299,6 @@ func mapClaimsToRpcContext(claims jwt.MapClaims) *wshrpc.RpcContext {
|
|||||||
rpcCtx.TabId = tabId
|
rpcCtx.TabId = tabId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if claims["windowid"] != nil {
|
|
||||||
if windowId, ok := claims["windowid"].(string); ok {
|
|
||||||
rpcCtx.WindowId = windowId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rpcCtx
|
return rpcCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user