Skip to content

Commit

Permalink
Merge pull request #470 from gopcua/issue-430-panic-on-closed-channel
Browse files Browse the repository at this point in the history
guard against double close
  • Loading branch information
magiconair authored Nov 11, 2021
2 parents 0c56d7f + 6689798 commit 809fb04
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 3 deletions.
8 changes: 8 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@ func (c *Client) monitor(ctx context.Context) {
// a reconnection to the server

// close previous secure channel
//
// todo(fs): the two calls to Close() trigger a double-close on both the
// todo(fs): secure channel and the UACP connection. I have guarded for this
// todo(fs): with a sync.Once but that feels like a band-aid. We need to investigate
// todo(fs): why we are trying to create a new secure channel when we shut the client
// todo(fs): down.
//
// https://github.com/gopcua/opcua/pull/470
_ = c.conn.Close()
c.sechan.Close()
c.sechan = nil
Expand Down
13 changes: 11 additions & 2 deletions uacp/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"net"
"sync"
"sync/atomic"

"github.com/gopcua/opcua/debug"
Expand Down Expand Up @@ -144,7 +145,7 @@ func (l *Listener) Accept(ctx context.Context) (*Conn, error) {
if err != nil {
return nil, err
}
conn := &Conn{c, nextid(), l.ack}
conn := &Conn{TCPConn: c, id: nextid(), ack: l.ack}
if err := conn.srvhandshake(l.endpoint); err != nil {
c.Close()
return nil, err
Expand All @@ -171,6 +172,8 @@ type Conn struct {
*net.TCPConn
id uint32
ack *Acknowledge

closeOnce sync.Once
}

func NewConn(c *net.TCPConn, ack *Acknowledge) (*Conn, error) {
Expand Down Expand Up @@ -203,7 +206,13 @@ func (c *Conn) MaxChunkCount() uint32 {
return c.ack.MaxChunkCount
}

func (c *Conn) Close() error {
func (c *Conn) Close() (err error) {
err = io.EOF
c.closeOnce.Do(func() { err = c.close() })
return err
}

func (c *Conn) close() error {
debug.Printf("conn %d: close", c.id)
return c.TCPConn.Close()
}
Expand Down
12 changes: 11 additions & 1 deletion uasc/secure_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ type SecureChannel struct {

// errorCh receive dispatcher errors
errCh chan<- error

closeOnce sync.Once
}

func NewSecureChannel(endpoint string, c *uacp.Conn, cfg *Config, errCh chan<- error) (*SecureChannel, error) {
Expand Down Expand Up @@ -819,7 +821,15 @@ func (s *SecureChannel) nextRequestID() uint32 {
}

// Close closes an existing secure channel
func (s *SecureChannel) Close() error {
func (s *SecureChannel) Close() (err error) {
// https://github.com/gopcua/opcua/pull/470
// guard against double close until we found the root cause
err = io.EOF
s.closeOnce.Do(func() { err = s.close() })
return
}

func (s *SecureChannel) close() error {
debug.Printf("uasc Close()")

defer func() {
Expand Down

0 comments on commit 809fb04

Please sign in to comment.