Skip to content

Commit 86a3a66

Browse files
ImeevMAbigbes
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 66ef721 commit 86a3a66

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
@@ -14,6 +14,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1414

1515
### Fixed
1616

17+
- Fixed panic when calling NewWatcher() during reconnection or after
18+
connection is closed (#438).
19+
1720
## [v2.3.2] - 2025-04-14
1821

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