2021-08-18 15:30:55 +02:00
// Copyright (c) HashiCorp, Inc.
2022-07-06 13:37:49 -07:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2021-08-18 15:30:55 +02:00
package session
import (
"context"
2022-09-22 17:51:30 -07:00
"crypto"
2022-07-06 13:37:49 -07:00
"crypto/x509"
2021-08-18 15:30:55 +02:00
"errors"
"fmt"
"sync"
"time"
2023-09-12 08:15:34 -04:00
"github.com/hashicorp/boundary/internal/event"
2021-08-18 15:30:55 +02:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
"github.com/hashicorp/boundary/internal/session"
)
// ValidateSessionTimeout is the duration of the timeout when the worker queries the
// controller for the sessionId for which the connection is being requested.
const ValidateSessionTimeout = 90 * time . Second
var errMakeSessionCloseInfoNilCloseInfo = errors . New ( "nil closeInfo supplied to makeSessionCloseInfo, this is a bug, please report it" )
// ConnInfo defines the information about a connection attached to a session
type ConnInfo struct {
2022-07-06 13:37:49 -07:00
Id string
Status pbs . CONNECTIONSTATUS
// The context.CancelFunc for the proxy connection. Calling this function
// closes the proxy connection.
connCtxCancelFunc context . CancelFunc
2022-10-05 13:20:35 +01:00
// The number of bytes uploaded from the client.
2022-10-06 17:37:50 +01:00
BytesUp func () int64
2022-10-05 13:20:35 +01:00
// The number of bytes downloaded to the client.
2022-10-06 17:37:50 +01:00
BytesDown func () int64
2022-10-05 13:20:35 +01:00
2022-07-06 13:37:49 -07:00
// The time the controller has successfully reported that this connection is
// closed.
CloseTime time . Time
}
2022-10-05 13:20:35 +01:00
// ConnectionCloseData encapsulates the data we need to send via CloseConnection
// RPC to the controller.
type ConnectionCloseData struct {
SessionId string
2022-10-06 17:37:50 +01:00
BytesUp int64
BytesDown int64
2022-10-05 13:20:35 +01:00
}
2022-07-06 13:37:49 -07:00
// Session is the local representation of a session. After initial loading
// the only values that will change will be the status (readable from
// GetStatus()) and the Connections (GetLocalConnections()).
type Session interface {
// ApplyLocalConnectionStatus set's a connection's status to the one provided.
// If there is no connection with the provided id, an error is returned.
ApplyLocalConnectionStatus ( connId string , status pbs . CONNECTIONSTATUS ) error
// ApplyLocalStatus updates the given session with the status provided by
// the SessionJobInfo. It returns an error if any of the connections
// in the SessionJobInfo are not present, however, it still applies the
// status change to the session and the connections which are present.
ApplyLocalStatus ( st pbs . SESSIONSTATUS )
GetStatus () pbs . SESSIONSTATUS
// GetLocalConnections returns the connections this session is handling.
GetLocalConnections () map [ string ] ConnInfo
GetTofuToken () string
GetConnectionLimit () int32
GetEndpoint () string
2022-09-22 17:51:30 -07:00
GetHostKeys () ([] crypto . Signer , error )
2022-07-06 13:37:49 -07:00
GetCredentials () [] * pbs . Credential
GetExpiration () time . Time
GetCertificate () * x509 . Certificate
GetPrivateKey () [] byte
GetId () string
// CancelOpenLocalConnections closes the local connections in this session
//based on the connection's state by calling the connections context cancel
// function.
//
// The returned slice are connection ids that were closed.
CancelOpenLocalConnections () [] string
// CancelAllLocalConnections close connections regardless of connection's state
// by calling the connection context's CancelFunc.
//
// The returned slice is the connection ids which were closed.
CancelAllLocalConnections () [] string
// RequestCancel sends session cancellation request to the controller. If there is no
// error the local session's status is updated with the result of the cancel
// request
RequestCancel ( ctx context . Context ) error
// RequestActivate sends session activation request to the controller. The Session's
// status is then updated with the result of the call. After a successful
// call to RequestActivate, subsequent calls will fail.
RequestActivate ( ctx context . Context , tofu string ) error
2022-10-05 13:20:35 +01:00
// ApplyConnectionCounterCallbacks sets a connection's bytes up and bytes
// down callbacks to the provided functions. Both functions must be safe for
// concurrent use. If there is no connection with the provided id, an error
// is returned.
2022-10-06 17:37:50 +01:00
ApplyConnectionCounterCallbacks ( connId string , bytesUp func () int64 , bytesDown func () int64 ) error
2022-10-05 13:20:35 +01:00
2022-07-06 13:37:49 -07:00
// RequestAuthorizeConnection sends an AuthorizeConnection request to
// the controller.
// It is called by the worker handler after a connection has been received by
// the worker, and the session has been validated.
// The passed in context.CancelFunc is used to terminate any ongoing local proxy
// connections.
// The connection status is then viewable in this session's GetLocalConnections() call.
2022-12-06 12:44:00 -07:00
RequestAuthorizeConnection ( ctx context . Context , workerId string , connCancel context . CancelFunc ) ( * pbs . AuthorizeConnectionResponse , int32 , error )
2022-07-06 13:37:49 -07:00
// RequestConnectConnection sends a RequestConnectConnection request to the controller. It
// should only be called by the worker handler after a connection has been
// authorized. The local connection's status is updated with the result of the
// call.
RequestConnectConnection ( ctx context . Context , info * pbs . ConnectConnectionRequest ) error
}
type sess struct {
lock sync . RWMutex
client pbs . SessionServiceClient
connInfoMap map [ string ] * ConnInfo
resp * pbs . LookupSessionResponse
status pbs . SESSIONSTATUS
cert * x509 . Certificate
2022-08-24 09:03:29 -04:00
sessionId string
2023-01-24 09:24:03 -05:00
tofuToken string
2022-07-06 13:37:49 -07:00
}
func newSess ( client pbs . SessionServiceClient , resp * pbs . LookupSessionResponse ) ( * sess , error ) {
switch {
case isNil ( client ):
return nil , errors . New ( "SessionServiceClient is nil" )
case resp . GetExpiration (). AsTime (). Before ( time . Now ()):
return nil , fmt . Errorf ( "session is expired" )
}
parsedCert , err := x509 . ParseCertificate ( resp . GetAuthorization (). GetCertificate ())
if err != nil {
return nil , fmt . Errorf ( "error parsing session certificate: %w" , err )
}
s := & sess {
client : client ,
connInfoMap : make ( map [ string ] * ConnInfo ),
resp : resp ,
status : resp . GetStatus (),
cert : parsedCert ,
2022-08-24 09:03:29 -04:00
sessionId : resp . GetAuthorization (). GetSessionId (),
2022-07-06 13:37:49 -07:00
}
return s , nil
}
// ApplyLocalConnectionStatus Satisfies the Session interface
func ( s * sess ) ApplyLocalConnectionStatus ( connId string , status pbs . CONNECTIONSTATUS ) error {
s . lock . Lock ()
defer s . lock . Unlock ()
// Update connection status if there are any connections in
// the request.
connInfo , ok := s . connInfoMap [ connId ]
if ! ok {
return fmt . Errorf ( "could not find connection ID %q for session ID %q in local state" ,
connId ,
s . GetId ())
}
connInfo . Status = status
if connInfo . Status == pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_CLOSED {
connInfo . CloseTime = time . Now ()
}
return nil
}
2022-08-24 09:03:29 -04:00
func ( s * sess ) ApplySessionUpdate ( r * pbs . LookupSessionResponse ) {
s . lock . Lock ()
defer s . lock . Unlock ()
s . resp = r
s . status = r . Status
}
2022-07-06 13:37:49 -07:00
func ( s * sess ) ApplyLocalStatus ( st pbs . SESSIONSTATUS ) {
s . lock . Lock ()
defer s . lock . Unlock ()
s . status = st
}
2023-01-24 09:24:03 -05:00
func ( s * sess ) ApplyLocalTofuToken ( tt string ) {
s . lock . Lock ()
defer s . lock . Unlock ()
s . tofuToken = tt
}
2022-07-06 13:37:49 -07:00
func ( s * sess ) GetLocalConnections () map [ string ] ConnInfo {
s . lock . RLock ()
defer s . lock . RUnlock ()
res := make ( map [ string ] ConnInfo , len ( s . connInfoMap ))
// Returning the s.connInfoMap directly wouldn't be thread safe.
for k , v := range s . connInfoMap {
res [ k ] = ConnInfo {
Id : v . Id ,
Status : v . Status ,
CloseTime : v . CloseTime ,
2022-10-05 13:20:35 +01:00
BytesUp : v . BytesUp ,
BytesDown : v . BytesDown ,
2022-07-06 13:37:49 -07:00
}
}
return res
}
2024-02-16 08:30:16 -05:00
// Return local tofu token, if available; otherwise return the tofu token from the resp
2022-07-06 13:37:49 -07:00
func ( s * sess ) GetTofuToken () string {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2023-03-08 13:49:57 -05:00
if len ( s . tofuToken ) > 0 {
return s . tofuToken
}
if s . resp != nil {
return s . resp . GetTofuToken ()
}
return ""
2022-07-06 13:37:49 -07:00
}
func ( s * sess ) GetConnectionLimit () int32 {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . resp . GetConnectionLimit ()
}
func ( s * sess ) GetEndpoint () string {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . resp . GetEndpoint ()
}
2022-09-22 17:51:30 -07:00
func ( s * sess ) GetHostKeys () ([] crypto . Signer , error ) {
s . lock . RLock ()
2022-12-06 12:44:00 -07:00
defer s . lock . RUnlock ()
2022-09-22 17:51:30 -07:00
pkcs8Keys := s . resp . GetPkcs8HostKeys ()
var hostKeys [] crypto . Signer
for _ , hostKey := range pkcs8Keys {
p , err := x509 . ParsePKCS8PrivateKey ( hostKey )
if err != nil {
return nil , errors . New ( "error parsing host keys" )
}
hostKey , ok := p .( crypto . Signer )
if ! ok {
return nil , fmt . Errorf ( "unsupported host key %T" , p )
}
hostKeys = append ( hostKeys , hostKey )
}
return hostKeys , nil
}
2022-07-06 13:37:49 -07:00
func ( s * sess ) GetCredentials () [] * pbs . Credential {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . resp . GetCredentials ()
}
func ( s * sess ) GetStatus () pbs . SESSIONSTATUS {
s . lock . RLock ()
defer s . lock . RUnlock ()
return s . status
2021-08-18 15:30:55 +02:00
}
2022-07-06 13:37:49 -07:00
func ( s * sess ) GetExpiration () time . Time {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . resp . GetExpiration (). AsTime ()
2021-08-18 15:30:55 +02:00
}
2022-07-06 13:37:49 -07:00
func ( s * sess ) GetCertificate () * x509 . Certificate {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . cert
}
func ( s * sess ) GetPrivateKey () [] byte {
2022-08-24 09:03:29 -04:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
return s . resp . GetAuthorization (). GetPrivateKey ()
}
func ( s * sess ) GetId () string {
2022-08-24 09:03:29 -04:00
return s . sessionId
2022-07-06 13:37:49 -07:00
}
func ( s * sess ) RequestCancel ( ctx context . Context ) error {
st , err := cancel ( ctx , s . client , s . GetId ())
if err != nil {
return err
}
s . ApplyLocalStatus ( st )
return nil
}
func ( s * sess ) RequestActivate ( ctx context . Context , tofu string ) error {
st , err := activate ( ctx , s . client , s . GetId (), tofu , s . resp . GetVersion ())
if err != nil {
return err
}
2023-01-24 09:24:03 -05:00
s . ApplyLocalTofuToken ( tofu )
2022-07-06 13:37:49 -07:00
s . ApplyLocalStatus ( st )
return nil
}
2022-12-06 12:44:00 -07:00
func ( s * sess ) RequestAuthorizeConnection ( ctx context . Context , workerId string , connCancel context . CancelFunc ) ( * pbs . AuthorizeConnectionResponse , int32 , error ) {
2022-07-06 13:37:49 -07:00
switch {
case connCancel == nil :
2022-12-06 12:44:00 -07:00
return nil , 0 , errors . New ( "the provided context.CancelFunc was nil" )
2022-07-06 13:37:49 -07:00
case workerId == "" :
2022-12-06 12:44:00 -07:00
return nil , 0 , errors . New ( "worker id is empty" )
2022-07-06 13:37:49 -07:00
}
2022-10-05 13:20:35 +01:00
2022-12-06 12:44:00 -07:00
resp , err := s . client . AuthorizeConnection ( ctx , & pbs . AuthorizeConnectionRequest {
SessionId : s . GetId (),
WorkerId : workerId ,
})
2022-07-06 13:37:49 -07:00
if err != nil {
2022-12-06 12:44:00 -07:00
return nil , 0 , fmt . Errorf ( "error authorizing connection: %w" , err )
}
ci := & ConnInfo {
Id : resp . GetConnectionId (),
Status : resp . GetStatus (),
connCtxCancelFunc : connCancel ,
2022-07-06 13:37:49 -07:00
}
2022-10-05 13:20:35 +01:00
// Install safe callbacks before connection has been established. These
// should be replaced when `ApplyConnectionCounterCallbacks` gets called on
// the `sess` object.
2022-10-06 17:37:50 +01:00
ci . BytesUp = func () int64 { return 0 }
ci . BytesDown = func () int64 { return 0 }
2022-10-05 13:20:35 +01:00
2022-07-06 13:37:49 -07:00
s . lock . Lock ()
defer s . lock . Unlock ()
s . connInfoMap [ ci . Id ] = ci
2022-12-06 12:44:00 -07:00
return resp , resp . GetConnectionsLeft (), err
2022-07-06 13:37:49 -07:00
}
func ( s * sess ) RequestConnectConnection ( ctx context . Context , info * pbs . ConnectConnectionRequest ) error {
st , err := connectConnection ( ctx , s . client , info )
if err != nil {
return err
}
2024-02-16 08:30:16 -05:00
if err := s . ApplyLocalConnectionStatus ( info . GetConnectionId (), st ); err != nil {
return fmt . Errorf ( "error applying local connection status: %w" , err )
}
2022-07-06 13:37:49 -07:00
return nil
}
// CancelOpenLocalConnections closes the local connections in this session
2022-08-25 09:12:24 -07:00
// based on the connection's state by calling the connections context cancel
2022-07-06 13:37:49 -07:00
// function.
//
// The returned slice are connection ids that were closed.
func ( s * sess ) CancelOpenLocalConnections () [] string {
2022-10-20 14:02:38 -07:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
var closedIds [] string
for k , v := range s . connInfoMap {
if v . Status != pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_CLOSED {
continue
}
v . connCtxCancelFunc ()
// CloseTime is set when the controller has reported that the connection
// is closed. The worker should only request a connection be marked
// closed after it has already been cancelled.
if v . CloseTime . IsZero () {
closedIds = append ( closedIds , k )
}
}
return closedIds
}
// CancelAllLocalConnections close connections regardless of connection's state
// by calling the connection context's CancelFunc.
//
// The returned slice is the connection ids which were closed.
func ( s * sess ) CancelAllLocalConnections () [] string {
2022-10-20 14:02:38 -07:00
s . lock . RLock ()
defer s . lock . RUnlock ()
2022-07-06 13:37:49 -07:00
var closedIds [] string
for k , v := range s . connInfoMap {
v . connCtxCancelFunc ()
// CloseTime is set when the controller has reported that the connection
// is closed. The worker should only request a connection be marked
// closed after it has already been cancelled.
if v . CloseTime . IsZero () {
closedIds = append ( closedIds , k )
}
}
return closedIds
}
2022-10-06 16:52:26 +01:00
// ApplyConnectionCounterCallbacks sets a connection's bytes up and bytes
// down callbacks to the provided functions. Both functions must be safe for
// concurrent use. If there is no connection with the provided id, an error
// is returned.
2022-10-06 17:37:50 +01:00
func ( s * sess ) ApplyConnectionCounterCallbacks ( connId string , bytesUp func () int64 , bytesDown func () int64 ) error {
2022-10-05 13:20:35 +01:00
s . lock . Lock ()
defer s . lock . Unlock ()
ci , ok := s . connInfoMap [ connId ]
if ! ok {
return fmt . Errorf ( "failed to find connection info for connection id %q" , connId )
}
ci . BytesUp = bytesUp
ci . BytesDown = bytesDown
return nil
}
2022-07-06 13:37:49 -07:00
// activate is a helper worker function that sends session activation request to the
2021-08-18 15:30:55 +02:00
// controller.
2022-07-06 13:37:49 -07:00
func activate ( ctx context . Context , sessClient pbs . SessionServiceClient , sessionId , tofuToken string , version uint32 ) ( pbs . SESSIONSTATUS , error ) {
2021-08-18 15:30:55 +02:00
resp , err := sessClient . ActivateSession ( ctx , & pbs . ActivateSessionRequest {
SessionId : sessionId ,
TofuToken : tofuToken ,
Version : version ,
})
if err != nil {
return pbs . SESSIONSTATUS_SESSIONSTATUS_UNSPECIFIED , fmt . Errorf ( "error activating session: %w" , err )
}
return resp . GetStatus (), nil
}
2022-07-06 13:37:49 -07:00
// cancel is a helper worker function that sends session cancellation request to the
2021-08-18 15:30:55 +02:00
// controller.
2022-07-06 13:37:49 -07:00
func cancel ( ctx context . Context , sessClient pbs . SessionServiceClient , sessionId string ) ( pbs . SESSIONSTATUS , error ) {
2021-08-18 15:30:55 +02:00
resp , err := sessClient . CancelSession ( ctx , & pbs . CancelSessionRequest {
SessionId : sessionId ,
})
if err != nil {
return pbs . SESSIONSTATUS_SESSIONSTATUS_UNSPECIFIED , fmt . Errorf ( "error canceling session: %w" , err )
}
return resp . GetStatus (), nil
}
2022-07-06 13:37:49 -07:00
// connectConnection is a helper worker function that sends connection
2021-08-18 15:30:55 +02:00
// connect request to the controller. It is called by the worker handler after a
// connection has been authorized.
2022-07-06 13:37:49 -07:00
func connectConnection ( ctx context . Context , sessClient pbs . SessionServiceClient , req * pbs . ConnectConnectionRequest ) ( pbs . CONNECTIONSTATUS , error ) {
2021-08-18 15:30:55 +02:00
resp , err := sessClient . ConnectConnection ( ctx , req )
if err != nil {
return pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_UNSPECIFIED , err
}
if resp . GetStatus () != pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_CONNECTED {
return pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_UNSPECIFIED , fmt . Errorf ( "unexpected state returned: %v" , resp . GetStatus (). String ())
}
return resp . GetStatus (), nil
}
2022-07-06 13:37:49 -07:00
// TODO: Move these to manager.go. This is kept here for now simply to make it easier to see the diff.
2021-08-18 15:30:55 +02:00
func closeConnection ( ctx context . Context , sessClient pbs . SessionServiceClient , req * pbs . CloseConnectionRequest ) ( * pbs . CloseConnectionResponse , error ) {
const op = "session.closeConnection"
resp , err := sessClient . CloseConnection ( ctx , req )
if err != nil {
return nil , err
}
if len ( resp . GetCloseResponseData ()) != len ( req . GetCloseRequestData ()) {
event . WriteError ( ctx , op , errors . New ( "mismatched number of states returned on connection closed" ), event . WithInfo ( "expected" , len ( req . GetCloseRequestData ()), "got" , len ( resp . GetCloseResponseData ())))
}
return resp , nil
}
2022-07-06 13:37:49 -07:00
// closeConnections is a helper worker function that sends connection close
2022-06-17 16:20:01 -04:00
// requests to the controller, and sets close times within the worker. It is
// called during the worker status loop and on connection exit on the proxy.
2021-08-18 15:30:55 +02:00
//
2022-06-17 16:20:01 -04:00
// The boolean indicates whether the function was successful, e.g. had any
// errors. Individual events will be sent for the errors if there are any.
//
2022-10-05 13:20:35 +01:00
// closeInfo is a map of connection id to connection metadata.
func closeConnections ( ctx context . Context , sessClient pbs . SessionServiceClient , sManager Manager , closeInfo map [ string ] * ConnectionCloseData ) bool {
2022-07-06 13:37:49 -07:00
const op = "session.closeConnections"
if len ( closeInfo ) == 0 {
2021-08-18 15:30:55 +02:00
// This should not happen, but it's a no-op if it does. Just
// return.
2022-06-17 16:20:01 -04:00
return false
2021-08-18 15:30:55 +02:00
}
// How we handle close info depends on whether or not we succeeded with
// marking them closed on the controller.
var sessionCloseInfo map [ string ][] * pbs . CloseConnectionResponseData
var err error
// TODO: This, along with the status call to the controller, probably needs a
// bit of formalization in terms of how we handle timeouts. For now, this
// just ensures consistency with the same status call in that it times out
// within an adequate period of time.
2022-11-10 13:14:57 -05:00
closeConnCtx , closeConnCancel := context . WithTimeout ( ctx , time . Duration ( CloseCallTimeout . Load ()))
2021-08-18 15:30:55 +02:00
defer closeConnCancel ()
response , err := closeConnection ( closeConnCtx , sessClient , makeCloseConnectionRequest ( closeInfo ))
if err != nil {
event . WriteError ( ctx , op , err , event . WithInfoMsg ( "error marking connections closed" ,
"warning" , "error contacting controller, connections will be closed only on worker" ,
"session_and_connection_ids" , fmt . Sprintf ( "%#v" , closeInfo ),
2022-11-10 13:14:57 -05:00
"timeout" , time . Duration ( CloseCallTimeout . Load ()). String (),
2021-08-18 15:30:55 +02:00
))
// Since we could not reach the controller, we have to make a "fake" response set.
sessionCloseInfo , err = makeFakeSessionCloseInfo ( closeInfo )
} else {
// Connection succeeded, so we can proceed with making the sessionCloseInfo
// off of the response data.
sessionCloseInfo , err = makeSessionCloseInfo ( closeInfo , response )
}
if err != nil {
event . WriteError ( ctx , op , err , event . WithInfoMsg ( "serious error in processing return data from controller, aborting additional session/connection state modification" ))
2022-06-17 16:20:01 -04:00
return false
2021-08-18 15:30:55 +02:00
}
// Mark connections as closed
2022-07-06 13:37:49 -07:00
_ , errs := setCloseTimeForResponse ( sManager , sessionCloseInfo )
2021-08-18 15:30:55 +02:00
if len ( errs ) > 0 {
for _ , err := range errs {
event . WriteError ( ctx , op , err , event . WithInfoMsg ( "error marking connection closed in state" ))
}
2022-06-17 16:20:01 -04:00
return false
2021-08-18 15:30:55 +02:00
}
2022-06-17 16:20:01 -04:00
return true
2021-08-18 15:30:55 +02:00
}
// makeCloseConnectionRequest creates a CloseConnectionRequest for
// use with closing connections.
//
// closeInfo is a map, indexed by connection ID, to the individual
// sessions IDs that those connections belong to. The values are
// ignored; the parameter is expected as such just for convenience of
// its caller.
2022-10-05 13:20:35 +01:00
func makeCloseConnectionRequest ( closeInfo map [ string ] * ConnectionCloseData ) * pbs . CloseConnectionRequest {
2021-08-18 15:30:55 +02:00
closeData := make ([] * pbs . CloseConnectionRequestData , 0 , len ( closeInfo ))
2022-10-05 13:20:35 +01:00
for connId , data := range closeInfo {
2021-08-18 15:30:55 +02:00
closeData = append ( closeData , & pbs . CloseConnectionRequestData {
ConnectionId : connId ,
Reason : session . UnknownReason . String (),
2022-10-05 13:20:35 +01:00
BytesUp : data . BytesUp ,
BytesDown : data . BytesDown ,
2021-08-18 15:30:55 +02:00
})
}
return & pbs . CloseConnectionRequest {
CloseRequestData : closeData ,
}
}
2022-07-06 13:37:49 -07:00
// makeSessionCloseInfo takes the response from closeConnections and
2021-08-18 15:30:55 +02:00
// our original closeInfo map and makes a map of slices, indexed by
// session ID, of all of the connection responses. This allows us to
// easily lock on session once for all connections in
// setCloseTimeForResponse.
func makeSessionCloseInfo (
2022-10-05 13:20:35 +01:00
closeInfo map [ string ] * ConnectionCloseData ,
2021-08-18 15:30:55 +02:00
response * pbs . CloseConnectionResponse ,
) ( map [ string ][] * pbs . CloseConnectionResponseData , error ) {
if closeInfo == nil {
return nil , errMakeSessionCloseInfoNilCloseInfo
}
result := make ( map [ string ][] * pbs . CloseConnectionResponseData )
for _ , v := range response . GetCloseResponseData () {
2022-10-05 13:20:35 +01:00
sessionId := closeInfo [ v . GetConnectionId ()]. SessionId
result [ sessionId ] = append ( result [ sessionId ], v )
2021-08-18 15:30:55 +02:00
}
return result , nil
}
// makeFakeSessionCloseInfo makes a "fake" makeFakeSessionCloseInfo, intended
// for use when we can't contact the controller.
func makeFakeSessionCloseInfo (
2022-10-05 13:20:35 +01:00
closeInfo map [ string ] * ConnectionCloseData ,
2021-08-18 15:30:55 +02:00
) ( map [ string ][] * pbs . CloseConnectionResponseData , error ) {
if closeInfo == nil {
return nil , errMakeSessionCloseInfoNilCloseInfo
}
result := make ( map [ string ][] * pbs . CloseConnectionResponseData )
2022-10-05 13:20:35 +01:00
for connectionId , closeData := range closeInfo {
sessionId := closeData . SessionId
2021-08-18 15:30:55 +02:00
result [ sessionId ] = append ( result [ sessionId ], & pbs . CloseConnectionResponseData {
ConnectionId : connectionId ,
Status : pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_CLOSED ,
})
}
return result , nil
}
// setCloseTimeForResponse iterates a CloseConnectionResponse and
// sets the close time for any connection found to be closed to the
// current time.
//
// sessionCloseInfo can be derived from the closeInfo supplied to
// makeCloseConnectionRequest through reverseCloseInfo, which creates
// a session ID to connection ID mapping.
//
// A non-zero error count does not necessarily mean the operation
// failed, as some connections may have been marked as closed. The
// actual list of connection IDs closed is returned as the first
// return value.
2022-07-06 13:37:49 -07:00
func setCloseTimeForResponse ( sManager Manager , sessionCloseInfo map [ string ][] * pbs . CloseConnectionResponseData ) ([] string , [] error ) {
2021-08-18 15:30:55 +02:00
closedIds := make ([] string , 0 )
2022-07-06 13:37:49 -07:00
var result [] error
2021-08-18 15:30:55 +02:00
for sessionId , responses := range sessionCloseInfo {
2022-07-06 13:37:49 -07:00
si := sManager . Get ( sessionId )
if si == nil {
result = append ( result , fmt . Errorf ( "could not find session ID %q in local state after closing connections" , sessionId ))
2021-08-18 15:30:55 +02:00
continue
}
2022-07-06 13:37:49 -07:00
connStatus := make ( map [ string ] pbs . CONNECTIONSTATUS , len ( responses ))
2021-08-18 15:30:55 +02:00
for _ , response := range responses {
2022-07-06 13:37:49 -07:00
if err := si . ApplyLocalConnectionStatus ( response . GetConnectionId (), response . GetStatus ()); err != nil {
result = append ( result , err )
2021-08-18 15:30:55 +02:00
continue
}
2022-07-06 13:37:49 -07:00
connStatus [ response . GetConnectionId ()] = response . GetStatus ()
if response . GetStatus () == pbs . CONNECTIONSTATUS_CONNECTIONSTATUS_CLOSED {
closedIds = append ( closedIds , response . GetConnectionId ())
2021-08-18 15:30:55 +02:00
}
}
}
2022-07-06 13:37:49 -07:00
return closedIds , result
2021-08-18 15:30:55 +02:00
}