waveterm/pkg/wshutil/wshmultiproxy.go

154 lines
4.0 KiB
Go
Raw Normal View History

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package wshutil
import (
"encoding/json"
"fmt"
"sync"
"github.com/google/uuid"
2024-11-21 03:05:13 +01:00
"github.com/wavetermdev/waveterm/pkg/panichandler"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
)
type multiProxyRouteInfo struct {
RouteId string
AuthToken string
Proxy *WshRpcProxy
RpcContext *wshrpc.RpcContext
}
// handles messages from multiple unauthenitcated clients
type WshRpcMultiProxy struct {
Lock *sync.Mutex
RouteInfo map[string]*multiProxyRouteInfo // authtoken to info
ToRemoteCh chan []byte
FromRemoteRawCh chan []byte // raw message from the remote
}
func MakeRpcMultiProxy() *WshRpcMultiProxy {
return &WshRpcMultiProxy{
Lock: &sync.Mutex{},
RouteInfo: make(map[string]*multiProxyRouteInfo),
ToRemoteCh: make(chan []byte, DefaultInputChSize),
FromRemoteRawCh: make(chan []byte, DefaultOutputChSize),
}
}
func (p *WshRpcMultiProxy) DisposeRoutes() {
p.Lock.Lock()
defer p.Lock.Unlock()
for authToken, routeInfo := range p.RouteInfo {
DefaultRouter.UnregisterRoute(routeInfo.RouteId)
delete(p.RouteInfo, authToken)
}
}
func (p *WshRpcMultiProxy) getRouteInfo(authToken string) *multiProxyRouteInfo {
p.Lock.Lock()
defer p.Lock.Unlock()
return p.RouteInfo[authToken]
}
func (p *WshRpcMultiProxy) setRouteInfo(authToken string, routeInfo *multiProxyRouteInfo) {
p.Lock.Lock()
defer p.Lock.Unlock()
p.RouteInfo[authToken] = routeInfo
}
func (p *WshRpcMultiProxy) removeRouteInfo(authToken string) {
p.Lock.Lock()
defer p.Lock.Unlock()
delete(p.RouteInfo, authToken)
}
func (p *WshRpcMultiProxy) sendResponseError(msg RpcMessage, sendErr error) {
if msg.ReqId == "" {
// no response needed
return
}
resp := RpcMessage{
ResId: msg.ReqId,
Error: sendErr.Error(),
}
respBytes, _ := json.Marshal(resp)
p.ToRemoteCh <- respBytes
}
func (p *WshRpcMultiProxy) sendAuthResponse(msg RpcMessage, routeId string, authToken string) {
if msg.ReqId == "" {
// no response needed
return
}
resp := RpcMessage{
ResId: msg.ReqId,
Data: wshrpc.CommandAuthenticateRtnData{RouteId: routeId, AuthToken: authToken},
}
respBytes, _ := json.Marshal(resp)
p.ToRemoteCh <- respBytes
}
func (p *WshRpcMultiProxy) handleUnauthMessage(msgBytes []byte) {
var msg RpcMessage
err := json.Unmarshal(msgBytes, &msg)
if err != nil {
// nothing to do here, malformed message
return
}
if msg.Command == wshrpc.Command_Authenticate {
rpcContext, routeId, err := handleAuthenticationCommand(msg)
if err != nil {
p.sendResponseError(msg, err)
return
}
routeInfo := &multiProxyRouteInfo{
RouteId: routeId,
AuthToken: uuid.New().String(),
RpcContext: rpcContext,
}
routeInfo.Proxy = MakeRpcProxy()
routeInfo.Proxy.SetRpcContext(rpcContext)
p.setRouteInfo(routeInfo.AuthToken, routeInfo)
p.sendAuthResponse(msg, routeId, routeInfo.AuthToken)
go func() {
2024-11-21 03:05:13 +01:00
defer panichandler.PanicHandler("WshRpcMultiProxy:handleUnauthMessage")
for msgBytes := range routeInfo.Proxy.ToRemoteCh {
p.ToRemoteCh <- msgBytes
}
}()
DefaultRouter.RegisterRoute(routeId, routeInfo.Proxy, true)
return
}
if msg.AuthToken == "" {
p.sendResponseError(msg, fmt.Errorf("no auth token"))
return
}
routeInfo := p.getRouteInfo(msg.AuthToken)
if routeInfo == nil {
p.sendResponseError(msg, fmt.Errorf("invalid auth token"))
return
}
if msg.Command != "" && msg.Source != routeInfo.RouteId {
p.sendResponseError(msg, fmt.Errorf("invalid source route for auth token"))
return
}
if msg.Command == wshrpc.Command_Dispose {
DefaultRouter.UnregisterRoute(routeInfo.RouteId)
p.removeRouteInfo(msg.AuthToken)
close(routeInfo.Proxy.ToRemoteCh)
close(routeInfo.Proxy.FromRemoteCh)
return
}
routeInfo.Proxy.FromRemoteCh <- msgBytes
}
func (p *WshRpcMultiProxy) RunUnauthLoop() {
// loop over unauthenticated message
// handle Authenicate commands, and pass authenticated messages to the AuthCh
for msgBytes := range p.FromRemoteRawCh {
p.handleUnauthMessage(msgBytes)
}
}