fix: prevent dangling XMPP connections during configuration updates
- Add getConfiguration() methods to both bridges for thread-safe config access - Refactor UpdateConfiguration() methods to prevent mutex deadlock by releasing lock before blocking operations - Fix XMPP bridge to properly disconnect existing bridgeClient before creating new one - Add comprehensive timeout support to XMPP client (30s connection, 10s operations, 5s ping) - Implement proper disconnection with offline presence - Update all interfaces to use *config.Configuration for type safety 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
65038fb7a2
commit
69a67704f4
6 changed files with 121 additions and 55 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config"
|
||||
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger"
|
||||
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model"
|
||||
mmModel "github.com/mattermost/mattermost/server/public/model"
|
||||
|
@ -196,7 +197,7 @@ func (m *BridgeManager) Shutdown() error {
|
|||
}
|
||||
|
||||
// OnPluginConfigurationChange propagates configuration changes to all registered bridges
|
||||
func (m *BridgeManager) OnPluginConfigurationChange(config any) error {
|
||||
func (m *BridgeManager) OnPluginConfigurationChange(config *config.Configuration) error {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
|
|
|
@ -52,11 +52,18 @@ func NewBridge(log logger.Logger, api plugin.API, kvstore kvstore.KVStore, cfg *
|
|||
return bridge
|
||||
}
|
||||
|
||||
// getConfiguration safely retrieves the current configuration
|
||||
func (b *mattermostBridge) getConfiguration() *config.Configuration {
|
||||
b.configMu.RLock()
|
||||
defer b.configMu.RUnlock()
|
||||
return b.config
|
||||
}
|
||||
|
||||
// UpdateConfiguration updates the bridge configuration
|
||||
func (b *mattermostBridge) UpdateConfiguration(newConfig any) error {
|
||||
cfg, ok := newConfig.(*config.Configuration)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid configuration type")
|
||||
func (b *mattermostBridge) UpdateConfiguration(cfg *config.Configuration) error {
|
||||
// Validate configuration using built-in validation
|
||||
if err := cfg.IsValid(); err != nil {
|
||||
return fmt.Errorf("invalid configuration: %w", err)
|
||||
}
|
||||
|
||||
b.configMu.Lock()
|
||||
|
|
|
@ -80,56 +80,50 @@ func (b *xmppBridge) createXMPPClient(cfg *config.Configuration) *xmppClient.Cli
|
|||
)
|
||||
}
|
||||
|
||||
// getConfiguration safely retrieves the current configuration
|
||||
func (b *xmppBridge) getConfiguration() *config.Configuration {
|
||||
b.configMu.RLock()
|
||||
defer b.configMu.RUnlock()
|
||||
return b.config
|
||||
}
|
||||
|
||||
// UpdateConfiguration updates the bridge configuration
|
||||
func (b *xmppBridge) UpdateConfiguration(newConfig any) error {
|
||||
cfg, ok := newConfig.(*config.Configuration)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid configuration type")
|
||||
// It handles validation and reconnection logic when the configuration changes
|
||||
func (b *xmppBridge) UpdateConfiguration(cfg *config.Configuration) error {
|
||||
// Validate configuration using built-in validation
|
||||
if err := cfg.IsValid(); err != nil {
|
||||
return fmt.Errorf("invalid configuration: %w", err)
|
||||
}
|
||||
|
||||
b.configMu.Lock()
|
||||
oldConfig := b.config
|
||||
b.config = cfg
|
||||
defer b.configMu.Unlock()
|
||||
// Get current config to check if restart is needed
|
||||
oldConfig := b.getConfiguration()
|
||||
|
||||
b.logger.LogInfo("XMPP bridge configuration updated")
|
||||
// Update configuration under lock, then release immediately
|
||||
b.configMu.Lock()
|
||||
b.config = cfg
|
||||
|
||||
// Initialize or update XMPP client with new configuration
|
||||
if cfg.EnableSync {
|
||||
if cfg.XMPPServerURL == "" || cfg.XMPPUsername == "" || cfg.XMPPPassword == "" {
|
||||
return fmt.Errorf("XMPP server URL, username, and password are required when sync is enabled")
|
||||
if !cfg.Equals(oldConfig) {
|
||||
if b.bridgeClient != nil && b.bridgeClient.Disconnect() != nil {
|
||||
b.logger.LogError("Failed to disconnect old XMPP bridge client")
|
||||
}
|
||||
|
||||
b.bridgeClient = b.createXMPPClient(cfg)
|
||||
} else {
|
||||
b.bridgeClient = nil
|
||||
}
|
||||
b.configMu.Unlock()
|
||||
|
||||
// Stop the bridge
|
||||
if err := b.Stop(); err != nil {
|
||||
b.logger.LogWarn("Error stopping bridge during restart", "error", err)
|
||||
}
|
||||
|
||||
// Check if we need to restart the bridge due to configuration changes
|
||||
wasConnected := b.connected.Load()
|
||||
needsRestart := oldConfig != nil && !oldConfig.Equals(cfg) && wasConnected
|
||||
|
||||
// Log the configuration change
|
||||
if needsRestart {
|
||||
b.logger.LogInfo("Configuration changed, restarting bridge")
|
||||
} else {
|
||||
b.logger.LogInfo("Configuration updated", "config", cfg)
|
||||
// Start the bridge with new configuration
|
||||
// Start() method already uses getConfiguration() safely
|
||||
if err := b.Start(); err != nil {
|
||||
b.logger.LogError("Failed to restart bridge with new configuration", "error", err)
|
||||
return fmt.Errorf("failed to restart bridge: %w", err)
|
||||
}
|
||||
|
||||
if needsRestart {
|
||||
b.logger.LogInfo("Configuration changed, restarting bridge")
|
||||
|
||||
// Stop the bridge
|
||||
if err := b.Stop(); err != nil {
|
||||
b.logger.LogWarn("Error stopping bridge during restart", "error", err)
|
||||
}
|
||||
|
||||
// Start the bridge with new configuration
|
||||
if err := b.Start(); err != nil {
|
||||
b.logger.LogError("Failed to restart bridge with new configuration", "error", err)
|
||||
return fmt.Errorf("failed to restart bridge: %w", err)
|
||||
}
|
||||
}
|
||||
b.logger.LogDebug("XMPP bridge configuration updated successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue