Skip to content

Commit 87fe307

Browse files
authored
Prevent logging full dsn to std err (#14)
1 parent 4064339 commit 87fe307

File tree

5 files changed

+202
-3
lines changed

5 files changed

+202
-3
lines changed

balancer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ func (c *balancer) get(shouldBalancing bool) *wrapper {
7474
// failure make a db node become failure and auto health tracking
7575
func (c *balancer) failure(w *wrapper, err error) {
7676
if c.dbs.remove(w) { // remove this node
77-
reportError(fmt.Sprintf("deactive connection:[%s] for health checking due to error", w.dsn), err)
77+
reportError(
78+
fmt.Sprintf("deactive connection:[%s] for health checking due to error", hostnameFromDSN(w.db.DriverName(), w.dsn)),
79+
err,
80+
)
7881

7982
select {
8083
case <-c.ctx.Done():

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ require (
55
github.com/hashicorp/go-multierror v1.1.1
66
github.com/jmoiron/sqlx v1.3.5
77
github.com/lib/pq v1.10.6
8-
github.com/stretchr/testify v1.7.1
8+
github.com/stretchr/testify v1.8.0
99
)
1010

1111
require (

go.sum

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A
1818
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1919
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2020
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
21-
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
21+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
2222
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
23+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
24+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
2325
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2426
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2527
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

parse_dsn.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copy from: https://github.com/lib/pq//conn.go
2+
3+
package mssqlx
4+
5+
import (
6+
"fmt"
7+
"unicode"
8+
9+
"github.com/go-sql-driver/mysql"
10+
)
11+
12+
// pqScanner implements a tokenizer for libpq-style option strings.
13+
type pqScanner struct {
14+
s []rune
15+
i int
16+
}
17+
18+
// newPQScanner returns a new scanner initialized with the option string s.
19+
func newPQScanner(s string) *pqScanner {
20+
return &pqScanner{[]rune(s), 0}
21+
}
22+
23+
// Next returns the next rune.
24+
// It returns 0, false if the end of the text has been reached.
25+
func (s *pqScanner) Next() (rune, bool) {
26+
if s.i >= len(s.s) {
27+
return 0, false
28+
}
29+
r := s.s[s.i]
30+
s.i++
31+
return r, true
32+
}
33+
34+
// SkipSpaces returns the next non-whitespace rune.
35+
// It returns 0, false if the end of the text has been reached.
36+
func (s *pqScanner) SkipSpaces() (rune, bool) {
37+
r, ok := s.Next()
38+
for unicode.IsSpace(r) && ok {
39+
r, ok = s.Next()
40+
}
41+
return r, ok
42+
}
43+
44+
// parses the postgres-options from name and adds them to the values.
45+
//
46+
// The parsing code is based on conninfo_parse from libpq's fe-connect.c
47+
func parsePQOpts(name string, o map[string]string) error {
48+
s := newPQScanner(name)
49+
50+
for {
51+
var (
52+
keyRunes, valRunes []rune
53+
r rune
54+
ok bool
55+
)
56+
57+
if r, ok = s.SkipSpaces(); !ok {
58+
break
59+
}
60+
61+
// Scan the key
62+
for !unicode.IsSpace(r) && r != '=' {
63+
keyRunes = append(keyRunes, r)
64+
if r, ok = s.Next(); !ok {
65+
break
66+
}
67+
}
68+
69+
// Skip any whitespace if we're not at the = yet
70+
if r != '=' {
71+
r, ok = s.SkipSpaces()
72+
}
73+
74+
// The current character should be =
75+
if r != '=' || !ok {
76+
return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes))
77+
}
78+
79+
// Skip any whitespace after the =
80+
if r, ok = s.SkipSpaces(); !ok {
81+
// If we reach the end here, the last value is just an empty string as per libpq.
82+
o[string(keyRunes)] = ""
83+
break
84+
}
85+
86+
if r != '\'' {
87+
for !unicode.IsSpace(r) {
88+
if r == '\\' {
89+
if r, ok = s.Next(); !ok {
90+
return fmt.Errorf(`missing character after backslash`)
91+
}
92+
}
93+
valRunes = append(valRunes, r)
94+
95+
if r, ok = s.Next(); !ok {
96+
break
97+
}
98+
}
99+
} else {
100+
quote:
101+
for {
102+
if r, ok = s.Next(); !ok {
103+
return fmt.Errorf(`unterminated quoted string literal in connection string`)
104+
}
105+
switch r {
106+
case '\'':
107+
break quote
108+
case '\\':
109+
r, _ = s.Next()
110+
fallthrough
111+
default:
112+
valRunes = append(valRunes, r)
113+
}
114+
}
115+
}
116+
117+
o[string(keyRunes)] = string(valRunes)
118+
}
119+
120+
return nil
121+
}
122+
123+
// converts a url to a connection string for driver.Open.
124+
// Example:
125+
//
126+
// "postgres://bob:[email protected]:5432/mydb?sslmode=verify-full"
127+
//
128+
// converts to:
129+
//
130+
// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full"
131+
//
132+
// A minimal example:
133+
//
134+
// "postgres://"
135+
//
136+
// This will be blank, causing driver.Open to use all of the defaults
137+
func parsePostgresDSN(dsn string) (string, error) {
138+
meta := make(map[string]string)
139+
140+
if err := parsePQOpts(dsn, meta); err != nil {
141+
return "", err
142+
}
143+
144+
host, port := meta["host"], meta["port"]
145+
if host == "" && port == "" {
146+
return "", nil
147+
}
148+
149+
return fmt.Sprintf("%s:%s", host, port), nil
150+
}
151+
152+
func hostnameFromDSN(driverName, dsn string) string {
153+
switch driverName {
154+
case "mysql":
155+
if cf, err := mysql.ParseDSN(dsn); err == nil {
156+
return fmt.Sprintf("%s(%s)", cf.Net, cf.Addr)
157+
}
158+
159+
case "postgres":
160+
if host, err := parsePostgresDSN(dsn); err == nil {
161+
return host
162+
}
163+
}
164+
165+
return ""
166+
}

parse_dsn_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package mssqlx
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestParseDSN(t *testing.T) {
10+
require.Equal(t, "127.0.0.1:5555",
11+
hostnameFromDSN("postgres", "host=127.0.0.1 port=5555 user=root password=password dbname=testdb sslmode=disable"))
12+
13+
require.Equal(t, "",
14+
hostnameFromDSN("postgres", "a=b c=d"))
15+
16+
require.Equal(t, "",
17+
hostnameFromDSN("postgres", "a:b"))
18+
19+
require.Equal(t, "tcp(172.17.0.2:3306)",
20+
hostnameFromDSN("mysql", "user:password@(172.17.0.2:3306)/practice?charset=utf8mb4&interpolateParams=true"))
21+
22+
require.Equal(t, "udp(172.17.0.2:3306)",
23+
hostnameFromDSN("mysql", "user:password@udp(172.17.0.2:3306)/practice?charset=utf8mb4&interpolateParams=true"))
24+
25+
require.Equal(t, "",
26+
hostnameFromDSN("mysql", "a=b c=d"))
27+
28+
}

0 commit comments

Comments
 (0)