Repository: denisenkom/go-mssqldb
Pull request #642 (2021-02-24 15:39:28)

Workaround for Go 1.16
This PR should workaround the issues regarding to the tls changes in Go 1.16.

**This is by no means a proper fix**
I do not have enough knowledge about the foundation of the tls package. I can only confirm that the changes fixed my issues. 

robinknaapen: I noticed https://github.com/denisenkom/go-mssqldb/pull/640, this PR contains infinite recursion on `tlsHandshakeConn`

This should fix the issues in https://github.com/denisenkom/go-mssqldb/issues/639
codecov[bot]: # [Codecov](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=h1) Report
> Merging [#642](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=desc) (00eb26b) into [master](https://codecov.io/gh/denisenkom/go-mssqldb/commit/045585d74f9069afe2e115b6235eb043c8047043?el=desc) (045585d) will **decrease** coverage by `0.10%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642/graphs/tree.svg?width=650&height=150&src=pr&token=MoMB2u5avp)](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #642      +/-   ##
==========================================
- Coverage   71.98%   71.87%   -0.11%     
==========================================
  Files          24       24              
  Lines        5469     5469              
==========================================
- Hits         3937     3931       -6     
- Misses       1309     1313       +4     
- Partials      223      225       +2     
```


| [Impacted Files](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [net.go](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642/diff?src=pr&el=tree#diff-bmV0Lmdv) | `42.52% <0.00%> (ø)` | |
| [tds.go](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642/diff?src=pr&el=tree#diff-dGRzLmdv) | `66.83% <0.00%> (-0.77%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=footer). Last update [045585d...00eb26b](https://codecov.io/gh/denisenkom/go-mssqldb/pull/642?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

uvw: Any chance we could see this PR reviewed/merged soon?
kedlas: Hope to see this merged soon too.
prochac: Author's GitHub activity seems pretty poor.

Patch:
diff --git a/appveyor.yml b/appveyor.yml
index dfcb62d..ecb893a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -39,6 +39,9 @@ environment:
     - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
       GOVERSION: 115
       SQLINSTANCE: SQL2017
+    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      GOVERSION: 116
+      SQLINSTANCE: SQL2017
 
 install:
   - set GOROOT=c:\go%GOVERSION%
diff --git a/net.go b/net.go
index dccfde3..bb7b784 100644
--- a/net.go
+++ b/net.go
@@ -7,8 +7,8 @@ import (
 )
 
 type timeoutConn struct {
-	c             net.Conn
-	timeout       time.Duration
+	c       net.Conn
+	timeout time.Duration
 }
 
 func newTimeoutConn(conn net.Conn, timeout time.Duration) *timeoutConn {
@@ -51,21 +51,21 @@ func (c timeoutConn) RemoteAddr() net.Addr {
 }
 
 func (c timeoutConn) SetDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetDeadline(t)
 }
 
 func (c timeoutConn) SetReadDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetReadDeadline(t)
 }
 
 func (c timeoutConn) SetWriteDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetWriteDeadline(t)
 }
 
 // this connection is used during TLS Handshake
 // TDS protocol requires TLS handshake messages to be sent inside TDS packets
 type tlsHandshakeConn struct {
-	buf *tdsBuffer
+	buf           *tdsBuffer
 	packetPending bool
 	continueRead  bool
 }
@@ -105,27 +105,27 @@ func (c *tlsHandshakeConn) Write(b []byte) (n int, err error) {
 }
 
 func (c *tlsHandshakeConn) Close() error {
-	panic("Not implemented")
+	return c.buf.transport.Close()
 }
 
 func (c *tlsHandshakeConn) LocalAddr() net.Addr {
-	panic("Not implemented")
+	return nil
 }
 
 func (c *tlsHandshakeConn) RemoteAddr() net.Addr {
-	panic("Not implemented")
+	return nil
 }
 
-func (c *tlsHandshakeConn) SetDeadline(t time.Time) error {
-	panic("Not implemented")
+func (c *tlsHandshakeConn) SetDeadline(_ time.Time) error {
+	return nil
 }
 
-func (c *tlsHandshakeConn) SetReadDeadline(t time.Time) error {
-	panic("Not implemented")
+func (c *tlsHandshakeConn) SetReadDeadline(_ time.Time) error {
+	return nil
 }
 
-func (c *tlsHandshakeConn) SetWriteDeadline(t time.Time) error {
-	panic("Not implemented")
+func (c *tlsHandshakeConn) SetWriteDeadline(_ time.Time) error {
+	return nil
 }
 
 // this connection just delegates all methods to it's wrapped connection
@@ -148,21 +148,21 @@ func (c passthroughConn) Close() error {
 }
 
 func (c passthroughConn) LocalAddr() net.Addr {
-	panic("Not implemented")
+	return c.c.LocalAddr()
 }
 
 func (c passthroughConn) RemoteAddr() net.Addr {
-	panic("Not implemented")
+	return c.c.RemoteAddr()
 }
 
 func (c passthroughConn) SetDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetDeadline(t)
 }
 
 func (c passthroughConn) SetReadDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetReadDeadline(t)
 }
 
 func (c passthroughConn) SetWriteDeadline(t time.Time) error {
-	panic("Not implemented")
+	return c.c.SetWriteDeadline(t)
 }


Test patch:
diff --git a/net_go116_test.go b/net_go116_test.go
new file mode 100644
index 0000000..79dc077
--- /dev/null
+++ b/net_go116_test.go
@@ -0,0 +1,172 @@
+// +build go1.16
+
+package mssql
+
+import (
+	"context"
+	"net"
+	"testing"
+	"time"
+)
+
+func assertPanic(t *testing.T, paniced bool) {
+	v := recover()
+	if paniced && v == nil {
+		t.Fatalf(`expected panic but it did not`)
+	}
+
+	if !paniced && v != nil {
+		t.Fatalf(`expected no panic but it did`)
+	}
+}
+
+func TestTimeoutConn(t *testing.T) {
+	_, conn := net.Pipe()
+
+	tconn := newTimeoutConn(conn, time.Minute)
+	t.Run(`set deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Millisecond * 100)
+
+		err := tconn.SetDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set read deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := tconn.SetReadDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetReadDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set write deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := tconn.SetWriteDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetWriteDeadline should return nil`)
+		}
+	})
+}
+
+func TestTLSHandshakeConn(t *testing.T) {
+	SetLogger(testLogger{t})
+
+	connector, err := NewConnector(makeConnStr(t).String())
+	if err != nil {
+		t.Error(err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
+	defer cancel()
+
+	toconn, err := dialConnection(ctx, connector, connector.params)
+	if err != nil {
+		t.Error(err)
+	}
+
+	outbuf := newTdsBuffer(connector.params.packetSize, toconn)
+	handshakeConn := tlsHandshakeConn{buf: outbuf}
+
+	t.Run(`set deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Millisecond * 100)
+
+		err := handshakeConn.SetDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set read deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := handshakeConn.SetReadDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetReadDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set write deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := handshakeConn.SetWriteDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetWriteDeadline should return nil`)
+		}
+	})
+
+	t.Run(`get remote addr`, func(t *testing.T) {
+		addr := handshakeConn.RemoteAddr()
+		if addr != nil {
+			t.Fatalf(`RemoteAddr should return nil`)
+		}
+	})
+}
+
+func TestPassthroughConn(t *testing.T) {
+	SetLogger(testLogger{t})
+
+	connector, err := NewConnector(makeConnStr(t).String())
+	if err != nil {
+		t.Error(err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
+	defer cancel()
+
+	toconn, err := dialConnection(ctx, connector, connector.params)
+	if err != nil {
+		t.Error(err)
+	}
+
+	outbuf := newTdsBuffer(connector.params.packetSize, toconn)
+
+	handshakeConn := tlsHandshakeConn{buf: outbuf}
+	passthrough := passthroughConn{c: &handshakeConn}
+
+	t.Run(`set deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Millisecond * 100)
+
+		err := passthrough.SetDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set read deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := passthrough.SetReadDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetReadDeadline should return nil`)
+		}
+	})
+
+	t.Run(`set write deadline`, func(t *testing.T) {
+		defer assertPanic(t, false)
+		deadline := time.Now().Add(time.Minute)
+
+		err := passthrough.SetWriteDeadline(deadline)
+		if err != nil {
+			t.Fatalf(`SetWriteDeadline should return nil`)
+		}
+	})
+
+	t.Run(`get remote addr`, func(t *testing.T) {
+		addr := passthrough.RemoteAddr()
+		if addr != nil {
+			t.Fatalf(`RemoteAddr should return nil`)
+		}
+	})
+}
diff --git a/queries_test.go b/queries_test.go
index f89f901..0589491 100644
--- a/queries_test.go
+++ b/queries_test.go
@@ -2230,3 +2230,16 @@ func TestDisconnect2(t *testing.T) {
 		t.Fatal("timeout")
 	}
 }
+
+func TestClose(t *testing.T) {
+	conn := open(t)
+
+	defer func() {
+		v := recover()
+		if v != nil {
+			t.Fatal(`Close should not panic:`, v)
+		}
+	}()
+
+	conn.Close()
+}