Skip to content

Commit be273ee

Browse files
authored
Merge pull request #822 from famarting/fix-grpc-endpoint-parse-no-dns
fix grpc endpoint parse when no dns scheme is provided
2 parents 5b17222 + 828ed6b commit be273ee

File tree

2 files changed

+53
-33
lines changed

2 files changed

+53
-33
lines changed

client/internal/parse.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ import (
2121
"strings"
2222
)
2323

24+
const (
25+
dnsScheme = "dns"
26+
unixScheme = "unix"
27+
unixAbstractScheme = "unix-abstract"
28+
vsockScheme = "vsock"
29+
httpScheme = "http"
30+
httpsScheme = "https"
31+
)
32+
2433
// Parsed represents a parsed gRPC endpoint.
2534
type Parsed struct {
2635
Target string
@@ -37,6 +46,7 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
3746
var hostname string
3847
var tls bool
3948

49+
noSchemeInEndpoint := false
4050
urlSplit := strings.Split(target, ":")
4151
if len(urlSplit) == 3 && !strings.Contains(target, "://") {
4252
target = strings.Replace(target, ":", "://", 1)
@@ -46,13 +56,14 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
4656
urlSplit = strings.Split(target, "://")
4757
if len(urlSplit) == 1 {
4858
target = "dns://" + target
59+
noSchemeInEndpoint = true
4960
} else {
5061
scheme := urlSplit[0]
5162
if !schemeKnown(scheme) {
5263
return Parsed{}, fmt.Errorf(("unknown scheme: %q"), scheme)
5364
}
5465

55-
if scheme == "dns" {
66+
if scheme == dnsScheme {
5667
urlSplit = strings.Split(target, "/")
5768
if len(urlSplit) < 4 {
5869
return Parsed{}, fmt.Errorf("invalid dns scheme: %q", target)
@@ -79,7 +90,7 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
7990
}
8091

8192
if ptarget.Query().Has("tls") {
82-
if ptarget.Scheme == "http" || ptarget.Scheme == "https" {
93+
if ptarget.Scheme == httpScheme || ptarget.Scheme == httpsScheme {
8394
return Parsed{}, errors.New("cannot use tls query parameter with http(s) scheme")
8495
}
8596

@@ -92,11 +103,11 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
92103
}
93104

94105
scheme := ptarget.Scheme
95-
if scheme == "https" {
106+
if scheme == httpsScheme {
96107
tls = true
97108
}
98-
if scheme == "http" || scheme == "https" {
99-
scheme = "dns"
109+
if scheme == httpScheme || scheme == httpsScheme {
110+
scheme = dnsScheme
100111
}
101112

102113
hostname = ptarget.Host
@@ -112,28 +123,28 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
112123
}
113124

114125
if len(hostname) == 0 {
115-
if scheme == "dns" {
126+
if scheme == dnsScheme {
116127
hostname = "localhost"
117128
} else {
118129
hostname = ptarget.Path
119130
}
120131
}
121132

122133
switch scheme {
123-
case "unix":
134+
case unixScheme:
124135
separator := ":"
125136
if strings.HasPrefix(endpoint, "unix://") {
126137
separator = "://"
127138
}
128139
target = scheme + separator + hostname
129140

130-
case "vsock":
141+
case vsockScheme:
131142
target = scheme + ":" + hostname + ":" + port
132143

133-
case "unix-abstract":
144+
case unixAbstractScheme:
134145
target = scheme + ":" + hostname
135146

136-
case "dns":
147+
case dnsScheme:
137148
if len(ptarget.Path) > 0 {
138149
return Parsed{}, fmt.Errorf("path is not allowed: %q", ptarget.Path)
139150
}
@@ -142,9 +153,18 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
142153
hostname = "[" + hostname + "]"
143154
}
144155
if len(dnsAuthority) > 0 {
145-
dnsAuthority = "//" + dnsAuthority + "/"
156+
target = scheme + "://" + dnsAuthority + "/" + hostname + ":" + port
157+
} else if ptarget.Scheme == dnsScheme && !noSchemeInEndpoint {
158+
// when real scheme is dns, keep the previous format (dns:hostname:port)
159+
target = scheme + ":" + hostname + ":" + port
160+
} else {
161+
// Use passthrough resolver (just host:port) instead of the dns: scheme.
162+
// In gRPC v1.78, targets of the form dns:host:port can be treated as
163+
// opaque by grpc.DialContext(), so the DNS resolver is not invoked.
164+
// Using a bare host:port target makes grpc.DialContext() fall back to
165+
// the passthrough resolver, which resolves and connects correctly.
166+
target = hostname + ":" + port
146167
}
147-
target = scheme + ":" + dnsAuthority + hostname + ":" + port
148168

149169
default:
150170
return Parsed{}, fmt.Errorf("unsupported scheme: %q", scheme)
@@ -158,12 +178,12 @@ func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
158178

159179
func schemeKnown(scheme string) bool {
160180
for _, s := range []string{
161-
"dns",
162-
"unix",
163-
"unix-abstract",
164-
"vsock",
165-
"http",
166-
"https",
181+
dnsScheme,
182+
unixScheme,
183+
unixAbstractScheme,
184+
vsockScheme,
185+
httpScheme,
186+
httpsScheme,
167187
} {
168188
if scheme == s {
169189
return true

client/internal/parse_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,52 +31,52 @@ func TestParse(t *testing.T) {
3131
expError: true,
3232
},
3333
":5000": {
34-
expTarget: "dns:localhost:5000",
34+
expTarget: "localhost:5000",
3535
expTLS: false,
3636
expError: false,
3737
},
3838
":5000?tls=false": {
39-
expTarget: "dns:localhost:5000",
39+
expTarget: "localhost:5000",
4040
expTLS: false,
4141
expError: false,
4242
},
4343
":5000?tls=true": {
44-
expTarget: "dns:localhost:5000",
44+
expTarget: "localhost:5000",
4545
expTLS: true,
4646
expError: false,
4747
},
4848
"myhost": {
49-
expTarget: "dns:myhost:443",
49+
expTarget: "myhost:443",
5050
expTLS: false,
5151
expError: false,
5252
},
5353
"myhost?tls=false": {
54-
expTarget: "dns:myhost:443",
54+
expTarget: "myhost:443",
5555
expTLS: false,
5656
expError: false,
5757
},
5858
"myhost?tls=true": {
59-
expTarget: "dns:myhost:443",
59+
expTarget: "myhost:443",
6060
expTLS: true,
6161
expError: false,
6262
},
6363
"myhost:443": {
64-
expTarget: "dns:myhost:443",
64+
expTarget: "myhost:443",
6565
expTLS: false,
6666
expError: false,
6767
},
6868
"myhost:443?tls=false": {
69-
expTarget: "dns:myhost:443",
69+
expTarget: "myhost:443",
7070
expTLS: false,
7171
expError: false,
7272
},
7373
"myhost:443?tls=true": {
74-
expTarget: "dns:myhost:443",
74+
expTarget: "myhost:443",
7575
expTLS: true,
7676
expError: false,
7777
},
7878
"http://myhost": {
79-
expTarget: "dns:myhost:443",
79+
expTarget: "myhost:443",
8080
expTLS: false,
8181
expError: false,
8282
},
@@ -91,7 +91,7 @@ func TestParse(t *testing.T) {
9191
expError: true,
9292
},
9393
"http://myhost:443": {
94-
expTarget: "dns:myhost:443",
94+
expTarget: "myhost:443",
9595
expTLS: false,
9696
expError: false,
9797
},
@@ -106,7 +106,7 @@ func TestParse(t *testing.T) {
106106
expError: true,
107107
},
108108
"http://myhost:5000": {
109-
expTarget: "dns:myhost:5000",
109+
expTarget: "myhost:5000",
110110
expTLS: false,
111111
expError: false,
112112
},
@@ -121,7 +121,7 @@ func TestParse(t *testing.T) {
121121
expError: true,
122122
},
123123
"https://myhost:443": {
124-
expTarget: "dns:myhost:443",
124+
expTarget: "myhost:443",
125125
expTLS: true,
126126
expError: false,
127127
},
@@ -251,12 +251,12 @@ func TestParse(t *testing.T) {
251251
expError: false,
252252
},
253253
"https://[2001:db8:1f70::999:de8:7648:6e8]": {
254-
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:443",
254+
expTarget: "[2001:db8:1f70::999:de8:7648:6e8]:443",
255255
expTLS: true,
256256
expError: false,
257257
},
258258
"https://[2001:db8:1f70::999:de8:7648:6e8]:5000": {
259-
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:5000",
259+
expTarget: "[2001:db8:1f70::999:de8:7648:6e8]:5000",
260260
expTLS: true,
261261
expError: false,
262262
},

0 commit comments

Comments
 (0)