Skip to content

Commit 20494e9

Browse files
ImeevMAoleg-jukovec
authored andcommitted
api: fix panic in conn.NewWatcher()
Before this patch, `conn.c` was not checked for `nil` before calling its method. This could cause a panic if the connection was lost or closed. Closes #438
1 parent 953a5cf commit 20494e9

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1717

1818
### Fixed
1919

20+
- Fixed panic when calling NewWatcher() during reconnection or after
21+
connection is closed (#438).
22+
2023
## [v2.3.2] - 2025-04-14
2124

2225
This release improves the logic of `Connect` and `pool.Connect` in case of a

connection.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ func (conn *Connection) NewWatcher(key string, callback WatchCallback) (Watcher,
14611461
// That's why we can't just check the Tarantool response for an unsupported
14621462
// request error.
14631463
if !isFeatureInSlice(iproto.IPROTO_FEATURE_WATCHERS,
1464-
conn.c.ProtocolInfo().Features) {
1464+
conn.serverProtocolInfo.Features) {
14651465
err := fmt.Errorf("the feature %s must be supported by connection "+
14661466
"to create a watcher", iproto.IPROTO_FEATURE_WATCHERS)
14671467
return nil, err

tarantool_test.go

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3550,6 +3550,72 @@ func TestConnection_NewWatcher(t *testing.T) {
35503550
}
35513551
}
35523552

3553+
func newWatcherReconnectionPrepareTestConnection(t *testing.T) (*Connection, context.CancelFunc) {
3554+
t.Helper()
3555+
3556+
const server = "127.0.0.1:3015"
3557+
testDialer := dialer
3558+
testDialer.Address = server
3559+
3560+
inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{
3561+
Dialer: testDialer,
3562+
InitScript: "config.lua",
3563+
Listen: server,
3564+
WaitStart: 100 * time.Millisecond,
3565+
ConnectRetry: 10,
3566+
RetryTimeout: 500 * time.Millisecond,
3567+
})
3568+
t.Cleanup(func() { test_helpers.StopTarantoolWithCleanup(inst) })
3569+
if err != nil {
3570+
t.Fatalf("Unable to start Tarantool: %s", err)
3571+
}
3572+
3573+
ctx, cancel := test_helpers.GetConnectContext()
3574+
3575+
reconnectOpts := opts
3576+
reconnectOpts.Reconnect = 100 * time.Millisecond
3577+
reconnectOpts.MaxReconnects = 0
3578+
reconnectOpts.Notify = make(chan ConnEvent)
3579+
conn, err := Connect(ctx, testDialer, reconnectOpts)
3580+
if err != nil {
3581+
t.Fatalf("Connection was not established: %v", err)
3582+
}
3583+
3584+
test_helpers.StopTarantool(inst)
3585+
3586+
// Wait for reconnection process to be started.
3587+
for conn.ConnectedNow() {
3588+
time.Sleep(100 * time.Millisecond)
3589+
}
3590+
3591+
return conn, cancel
3592+
}
3593+
3594+
func TestNewWatcherDuringReconnect(t *testing.T) {
3595+
test_helpers.SkipIfWatchersUnsupported(t)
3596+
3597+
conn, cancel := newWatcherReconnectionPrepareTestConnection(t)
3598+
defer conn.Close()
3599+
defer cancel()
3600+
3601+
_, err := conn.NewWatcher("one", func(event WatchEvent) {})
3602+
assert.NotNil(t, err)
3603+
assert.ErrorContains(t, err, "client connection is not ready")
3604+
}
3605+
3606+
func TestNewWatcherAfterClose(t *testing.T) {
3607+
test_helpers.SkipIfWatchersUnsupported(t)
3608+
3609+
conn, cancel := newWatcherReconnectionPrepareTestConnection(t)
3610+
defer cancel()
3611+
3612+
_ = conn.Close()
3613+
3614+
_, err := conn.NewWatcher("one", func(event WatchEvent) {})
3615+
assert.NotNil(t, err)
3616+
assert.ErrorContains(t, err, "using closed connection")
3617+
}
3618+
35533619
func TestConnection_NewWatcher_noWatchersFeature(t *testing.T) {
35543620
test_helpers.SkipIfWatchersSupported(t)
35553621

@@ -4149,13 +4215,13 @@ func runTestMain(m *testing.M) int {
41494215
startOpts.MemtxUseMvccEngine = !isStreamUnsupported
41504216

41514217
inst, err := test_helpers.StartTarantool(startOpts)
4152-
defer test_helpers.StopTarantoolWithCleanup(inst)
4153-
41544218
if err != nil {
41554219
log.Printf("Failed to prepare test tarantool: %s", err)
41564220
return 1
41574221
}
41584222

4223+
defer test_helpers.StopTarantoolWithCleanup(inst)
4224+
41594225
return m.Run()
41604226
}
41614227

0 commit comments

Comments
 (0)