Skip to content

Commit a6a4988

Browse files
committed
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 c0a8ad3 commit a6a4988

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1919

2020
### Fixed
2121

22+
- Fixed panic when calling NewWatcher() during reconnection or after
23+
connection is closed (#438).
24+
2225
## [v2.3.0] - 2025-03-11
2326

2427
The release extends box.info responses and ConnectionPool.GetInfo return data.

connection.go

+3
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,9 @@ func isFeatureInSlice(expected iproto.Feature, actualSlice []iproto.Feature) boo
14541454
//
14551455
// Since 1.10.0
14561456
func (conn *Connection) NewWatcher(key string, callback WatchCallback) (Watcher, error) {
1457+
if conn.c == nil {
1458+
return nil, ClientError{ErrConnectionNotReady, "client connection is not ready"}
1459+
}
14571460
// We need to check the feature because the IPROTO_WATCH request is
14581461
// asynchronous. We do not expect any response from a Tarantool instance
14591462
// That's why we can't just check the Tarantool response for an unsupported

tarantool_test.go

+47
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, "client connection is not ready")
3598+
}
3599+
35533600
func TestConnection_NewWatcher_noWatchersFeature(t *testing.T) {
35543601
test_helpers.SkipIfWatchersSupported(t)
35553602

0 commit comments

Comments
 (0)