Skip to content

Commit 72da2b9

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 72da2b9

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-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: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3550,6 +3550,53 @@ func TestConnection_NewWatcher(t *testing.T) {
35503550
}
35513551
}
35523552

3553+
func TestNewWatcherDuringReconnectOrAfterClose(t *testing.T) {
3554+
test_helpers.SkipIfWatchersUnsupported(t)
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+
defer 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+
defer cancel()
3575+
3576+
reconnectOpts := opts
3577+
reconnectOpts.Reconnect = 100 * time.Millisecond
3578+
reconnectOpts.MaxReconnects = 0
3579+
reconnectOpts.Notify = make(chan ConnEvent)
3580+
conn, err := Connect(ctx, testDialer, reconnectOpts)
3581+
if err != nil {
3582+
t.Fatalf("Connection was not established: %v", err)
3583+
}
3584+
defer conn.Close()
3585+
test_helpers.StopTarantool(inst)
3586+
3587+
for conn.ConnectedNow() {
3588+
time.Sleep(100 * time.Millisecond)
3589+
}
3590+
_, err = conn.NewWatcher("one", func(event WatchEvent) {})
3591+
assert.NotNil(t, err)
3592+
assert.ErrorContains(t, err, "client connection is not ready")
3593+
3594+
conn.Close()
3595+
_, err = conn.NewWatcher("two", func(event WatchEvent) {})
3596+
assert.NotNil(t, err)
3597+
assert.ErrorContains(t, err, "using closed connection")
3598+
}
3599+
35533600
func TestConnection_NewWatcher_noWatchersFeature(t *testing.T) {
35543601
test_helpers.SkipIfWatchersSupported(t)
35553602

@@ -4149,13 +4196,13 @@ func runTestMain(m *testing.M) int {
41494196
startOpts.MemtxUseMvccEngine = !isStreamUnsupported
41504197

41514198
inst, err := test_helpers.StartTarantool(startOpts)
4152-
defer test_helpers.StopTarantoolWithCleanup(inst)
4153-
41544199
if err != nil {
41554200
log.Printf("Failed to prepare test tarantool: %s", err)
41564201
return 1
41574202
}
41584203

4204+
defer test_helpers.StopTarantoolWithCleanup(inst)
4205+
41594206
return m.Run()
41604207
}
41614208

0 commit comments

Comments
 (0)