refactor(grpc/client): simplify processEventStream with per-event handlers
- Extract eventHandlers dispatch table - Add per-event handlers: handleSlugChange, handleGetSessions, handleTerminateSession - Introduce sendNode helper to centralize send/error handling and preserve connection-error propagation - Add protoToTunnelType for tunnel-type validation - Map unknown proto.TunnelType to types.UNKNOWN in protoToTunnelType and return a descriptive error - Reduce boilerplate and improve readability of processEventStream
This commit is contained in:
@@ -210,84 +210,71 @@ func (c *Client) SubscribeEvents(ctx context.Context, identity, authToken string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) processEventStream(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events]) error {
|
func (c *Client) processEventStream(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events]) error {
|
||||||
|
handlers := c.eventHandlers(subscribe)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
recv, err := subscribe.Recv()
|
recv, err := subscribe.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch recv.GetType() {
|
|
||||||
case proto.EventType_SLUG_CHANGE:
|
handler, ok := handlers[recv.GetType()]
|
||||||
user := recv.GetSlugEvent().GetUser()
|
if !ok {
|
||||||
oldSlug := recv.GetSlugEvent().GetOld()
|
log.Printf("Unknown event type received: %v", recv.GetType())
|
||||||
newSlug := recv.GetSlugEvent().GetNew()
|
|
||||||
var userSession *session.SSHSession
|
|
||||||
userSession, err = c.sessionRegistry.Get(types.SessionKey{
|
|
||||||
Id: oldSlug,
|
|
||||||
Type: types.HTTP,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
errSend := subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
|
||||||
Payload: &proto.Node_SlugEventResponse{
|
|
||||||
SlugEventResponse: &proto.SlugChangeEventResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: err.Error(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if errSend != nil {
|
|
||||||
if c.isConnectionError(errSend) {
|
|
||||||
return errSend
|
|
||||||
}
|
|
||||||
log.Printf("non-connection send error for slug change failure: %v", errSend)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = c.sessionRegistry.Update(user, types.SessionKey{
|
|
||||||
Id: oldSlug,
|
if err = handler(recv); err != nil {
|
||||||
Type: types.HTTP,
|
|
||||||
}, types.SessionKey{
|
|
||||||
Id: newSlug,
|
|
||||||
Type: types.HTTP,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
errSend := subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
|
||||||
Payload: &proto.Node_SlugEventResponse{
|
|
||||||
SlugEventResponse: &proto.SlugChangeEventResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: err.Error(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if errSend != nil {
|
|
||||||
if c.isConnectionError(errSend) {
|
|
||||||
return errSend
|
|
||||||
}
|
|
||||||
log.Printf("non-connection send error for slug change failure: %v", errSend)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
userSession.GetInteraction().Redraw()
|
|
||||||
err = subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
|
||||||
Payload: &proto.Node_SlugEventResponse{
|
|
||||||
SlugEventResponse: &proto.SlugChangeEventResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if c.isConnectionError(err) {
|
|
||||||
log.Printf("connection error sending slug change success: %v", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("non-connection send error for slug change success: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case proto.EventType_GET_SESSIONS:
|
}
|
||||||
sessions := c.sessionRegistry.GetAllSessionFromUser(recv.GetGetSessionsEvent().GetIdentity())
|
|
||||||
|
func (c *Client) eventHandlers(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events]) map[proto.EventType]func(*proto.Events) error {
|
||||||
|
return map[proto.EventType]func(*proto.Events) error{
|
||||||
|
proto.EventType_SLUG_CHANGE: func(evt *proto.Events) error { return c.handleSlugChange(subscribe, evt) },
|
||||||
|
proto.EventType_GET_SESSIONS: func(evt *proto.Events) error { return c.handleGetSessions(subscribe, evt) },
|
||||||
|
proto.EventType_TERMINATE_SESSION: func(evt *proto.Events) error { return c.handleTerminateSession(subscribe, evt) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleSlugChange(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events], evt *proto.Events) error {
|
||||||
|
slugEvent := evt.GetSlugEvent()
|
||||||
|
user := slugEvent.GetUser()
|
||||||
|
oldSlug := slugEvent.GetOld()
|
||||||
|
newSlug := slugEvent.GetNew()
|
||||||
|
|
||||||
|
userSession, err := c.sessionRegistry.Get(types.SessionKey{Id: oldSlug, Type: types.HTTP})
|
||||||
|
if err != nil {
|
||||||
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
|
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
||||||
|
Payload: &proto.Node_SlugEventResponse{
|
||||||
|
SlugEventResponse: &proto.SlugChangeEventResponse{Success: false, Message: err.Error()},
|
||||||
|
},
|
||||||
|
}, "slug change failure response")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.sessionRegistry.Update(user, types.SessionKey{Id: oldSlug, Type: types.HTTP}, types.SessionKey{Id: newSlug, Type: types.HTTP}); err != nil {
|
||||||
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
|
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
||||||
|
Payload: &proto.Node_SlugEventResponse{
|
||||||
|
SlugEventResponse: &proto.SlugChangeEventResponse{Success: false, Message: err.Error()},
|
||||||
|
},
|
||||||
|
}, "slug change failure response")
|
||||||
|
}
|
||||||
|
|
||||||
|
userSession.GetInteraction().Redraw()
|
||||||
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
|
Type: proto.EventType_SLUG_CHANGE_RESPONSE,
|
||||||
|
Payload: &proto.Node_SlugEventResponse{
|
||||||
|
SlugEventResponse: &proto.SlugChangeEventResponse{Success: true, Message: ""},
|
||||||
|
},
|
||||||
|
}, "slug change success response")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleGetSessions(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events], evt *proto.Events) error {
|
||||||
|
sessions := c.sessionRegistry.GetAllSessionFromUser(evt.GetGetSessionsEvent().GetIdentity())
|
||||||
|
|
||||||
var details []*proto.Detail
|
var details []*proto.Detail
|
||||||
for _, ses := range sessions {
|
for _, ses := range sessions {
|
||||||
detail := ses.Detail()
|
detail := ses.Detail()
|
||||||
@@ -300,115 +287,75 @@ func (c *Client) processEventStream(subscribe grpc.BidiStreamingClient[proto.Nod
|
|||||||
StartedAt: timestamppb.New(detail.StartedAt),
|
StartedAt: timestamppb.New(detail.StartedAt),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
err = subscribe.Send(&proto.Node{
|
|
||||||
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
Type: proto.EventType_GET_SESSIONS,
|
Type: proto.EventType_GET_SESSIONS,
|
||||||
Payload: &proto.Node_GetSessionsEvent{
|
Payload: &proto.Node_GetSessionsEvent{
|
||||||
GetSessionsEvent: &proto.GetSessionsResponse{
|
GetSessionsEvent: &proto.GetSessionsResponse{Details: details},
|
||||||
Details: details,
|
|
||||||
},
|
},
|
||||||
},
|
}, "send get sessions response")
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if c.isConnectionError(err) {
|
|
||||||
log.Printf("connection error sending sessions success: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
log.Printf("non-connection send error for sessions success: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case proto.EventType_TERMINATE_SESSION:
|
|
||||||
user := recv.GetTerminateSessionEvent().GetUser()
|
|
||||||
tunnelTypeRaw := recv.GetTerminateSessionEvent().GetTunnelType()
|
|
||||||
slug := recv.GetTerminateSessionEvent().GetSlug()
|
|
||||||
|
|
||||||
var userSession *session.SSHSession
|
func (c *Client) handleTerminateSession(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events], evt *proto.Events) error {
|
||||||
var tunnelType types.TunnelType
|
terminate := evt.GetTerminateSessionEvent()
|
||||||
if tunnelTypeRaw == proto.TunnelType_HTTP {
|
user := terminate.GetUser()
|
||||||
tunnelType = types.HTTP
|
slug := terminate.GetSlug()
|
||||||
} else if tunnelTypeRaw == proto.TunnelType_TCP {
|
|
||||||
tunnelType = types.TCP
|
tunnelType, err := c.protoToTunnelType(terminate.GetTunnelType())
|
||||||
} else {
|
if err != nil {
|
||||||
err = subscribe.Send(&proto.Node{
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
Type: proto.EventType_TERMINATE_SESSION,
|
Type: proto.EventType_TERMINATE_SESSION,
|
||||||
Payload: &proto.Node_TerminateSessionEventResponse{
|
Payload: &proto.Node_TerminateSessionEventResponse{
|
||||||
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{
|
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{Success: false, Message: err.Error()},
|
||||||
Success: false,
|
|
||||||
Message: "unknown tunnel type recived",
|
|
||||||
},
|
},
|
||||||
},
|
}, "terminate session invalid tunnel type")
|
||||||
})
|
}
|
||||||
|
|
||||||
|
userSession, err := c.sessionRegistry.GetWithUser(user, types.SessionKey{Id: slug, Type: tunnelType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.isConnectionError(err) {
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
log.Printf("connection error sending sessions success: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Printf("non-connection send error for sessions success: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
userSession, err = c.sessionRegistry.GetWithUser(user, types.SessionKey{
|
|
||||||
Id: slug,
|
|
||||||
Type: tunnelType,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err = subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_TERMINATE_SESSION,
|
Type: proto.EventType_TERMINATE_SESSION,
|
||||||
Payload: &proto.Node_TerminateSessionEventResponse{
|
Payload: &proto.Node_TerminateSessionEventResponse{
|
||||||
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{
|
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{Success: false, Message: err.Error()},
|
||||||
Success: false,
|
|
||||||
Message: err.Error(),
|
|
||||||
},
|
},
|
||||||
},
|
}, "terminate session fetch failed")
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if c.isConnectionError(err) {
|
|
||||||
log.Printf("connection error sending sessions success: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
log.Printf("non-connection send error for sessions success: %v", err)
|
|
||||||
}
|
if err = userSession.GetLifecycle().Close(); err != nil {
|
||||||
continue
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
}
|
|
||||||
err = userSession.GetLifecycle().Close()
|
|
||||||
if err != nil {
|
|
||||||
err = subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_TERMINATE_SESSION,
|
Type: proto.EventType_TERMINATE_SESSION,
|
||||||
Payload: &proto.Node_TerminateSessionEventResponse{
|
Payload: &proto.Node_TerminateSessionEventResponse{
|
||||||
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{
|
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{Success: false, Message: err.Error()},
|
||||||
Success: false,
|
|
||||||
Message: err.Error(),
|
|
||||||
},
|
},
|
||||||
},
|
}, "terminate session close failed")
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if c.isConnectionError(err) {
|
|
||||||
log.Printf("connection error sending sessions success: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
log.Printf("non-connection send error for sessions success: %v", err)
|
|
||||||
}
|
return c.sendNode(subscribe, &proto.Node{
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = subscribe.Send(&proto.Node{
|
|
||||||
Type: proto.EventType_TERMINATE_SESSION,
|
Type: proto.EventType_TERMINATE_SESSION,
|
||||||
Payload: &proto.Node_TerminateSessionEventResponse{
|
Payload: &proto.Node_TerminateSessionEventResponse{
|
||||||
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{
|
TerminateSessionEventResponse: &proto.TerminateSessionEventResponse{Success: true, Message: ""},
|
||||||
Success: true,
|
|
||||||
Message: "",
|
|
||||||
},
|
},
|
||||||
},
|
}, "terminate session success response")
|
||||||
})
|
}
|
||||||
if err != nil {
|
|
||||||
|
func (c *Client) sendNode(subscribe grpc.BidiStreamingClient[proto.Node, proto.Events], node *proto.Node, context string) error {
|
||||||
|
if err := subscribe.Send(node); err != nil {
|
||||||
if c.isConnectionError(err) {
|
if c.isConnectionError(err) {
|
||||||
log.Printf("connection error sending sessions success: %v", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("non-connection send error for sessions success: %v", err)
|
log.Printf("%s: %v", context, err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) protoToTunnelType(t proto.TunnelType) (types.TunnelType, error) {
|
||||||
|
switch t {
|
||||||
|
case proto.TunnelType_HTTP:
|
||||||
|
return types.HTTP, nil
|
||||||
|
case proto.TunnelType_TCP:
|
||||||
|
return types.TCP, nil
|
||||||
default:
|
default:
|
||||||
log.Printf("Unknown event type received: %v", recv.GetType())
|
return types.UNKNOWN, fmt.Errorf("unknown tunnel type received")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const (
|
|||||||
type TunnelType string
|
type TunnelType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
UNKNOWN TunnelType = "UNKNOWN"
|
||||||
HTTP TunnelType = "HTTP"
|
HTTP TunnelType = "HTTP"
|
||||||
TCP TunnelType = "TCP"
|
TCP TunnelType = "TCP"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user