mirror of https://github.com/grpc/grpc-go.git
client: encode the authority by default (#6428)
This commit is contained in:
parent
11feb0a9af
commit
fc0aa4689c
|
@ -1818,6 +1818,61 @@ func parseTarget(target string) (resolver.Target, error) {
|
|||
return resolver.Target{URL: *u}, nil
|
||||
}
|
||||
|
||||
func encodeAuthority(authority string) string {
|
||||
const upperhex = "0123456789ABCDEF"
|
||||
|
||||
// Return for characters that must be escaped as per
|
||||
// Valid chars are mentioned here:
|
||||
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.2
|
||||
shouldEscape := func(c byte) bool {
|
||||
// Alphanum are always allowed.
|
||||
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
switch c {
|
||||
case '-', '_', '.', '~': // Unreserved characters
|
||||
return false
|
||||
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // Subdelim characters
|
||||
return false
|
||||
case ':', '[', ']', '@': // Authority related delimeters
|
||||
return false
|
||||
}
|
||||
// Everything else must be escaped.
|
||||
return true
|
||||
}
|
||||
|
||||
hexCount := 0
|
||||
for i := 0; i < len(authority); i++ {
|
||||
c := authority[i]
|
||||
if shouldEscape(c) {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
|
||||
if hexCount == 0 {
|
||||
return authority
|
||||
}
|
||||
|
||||
required := len(authority) + 2*hexCount
|
||||
t := make([]byte, required)
|
||||
|
||||
j := 0
|
||||
// This logic is a barebones version of escape in the go net/url library.
|
||||
for i := 0; i < len(authority); i++ {
|
||||
switch c := authority[i]; {
|
||||
case shouldEscape(c):
|
||||
t[j] = '%'
|
||||
t[j+1] = upperhex[c>>4]
|
||||
t[j+2] = upperhex[c&15]
|
||||
j += 3
|
||||
default:
|
||||
t[j] = authority[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
|
||||
// Determine channel authority. The order of precedence is as follows:
|
||||
// - user specified authority override using `WithAuthority` dial option
|
||||
// - creds' notion of server name for the authentication handshake
|
||||
|
@ -1868,7 +1923,11 @@ func (cc *ClientConn) determineAuthority() error {
|
|||
// the channel authority given the user's dial target. For resolvers
|
||||
// which don't implement this interface, we will use the endpoint from
|
||||
// "scheme://authority/endpoint" as the default authority.
|
||||
cc.authority = endpoint
|
||||
// Escape the endpoint to handle use cases where the endpoint
|
||||
// might not be a valid authority by default.
|
||||
// For example an endpoint which has multiple paths like
|
||||
// 'a/b/c', which is not a valid authority by default.
|
||||
cc.authority = encodeAuthority(endpoint)
|
||||
}
|
||||
channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority)
|
||||
return nil
|
||||
|
|
|
@ -1221,3 +1221,40 @@ func stayConnected(cc *ClientConn) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestURLAuthorityEscape(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
authority string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "ipv6_authority",
|
||||
authority: "[::1]",
|
||||
want: "[::1]",
|
||||
},
|
||||
{
|
||||
name: "with_user_and_host",
|
||||
authority: "userinfo@host:10001",
|
||||
want: "userinfo@host:10001",
|
||||
},
|
||||
{
|
||||
name: "with_multiple_slashes",
|
||||
authority: "projects/123/network/abc/service",
|
||||
want: "projects%2F123%2Fnetwork%2Fabc%2Fservice",
|
||||
},
|
||||
{
|
||||
name: "all_possible_allowed_chars",
|
||||
authority: "abc123-._~!$&'()*+,;=@:[]",
|
||||
want: "abc123-._~!$&'()*+,;=@:[]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got, want := encodeAuthority(test.authority), test.want; got != want {
|
||||
t.Errorf("encodeAuthority(%s) = %s, want %s", test.authority, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ var authorityTests = []authorityTest{
|
|||
name: "UnixPassthrough",
|
||||
address: "/tmp/sock.sock",
|
||||
target: "passthrough:///unix:///tmp/sock.sock",
|
||||
authority: "unix:///tmp/sock.sock",
|
||||
authority: "unix:%2F%2F%2Ftmp%2Fsock.sock",
|
||||
dialTargetWant: "unix:///tmp/sock.sock",
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue