Skip to content

Commit

Permalink
Add uri authority encode utils 191 (#192)
Browse files Browse the repository at this point in the history
* Add a util functions to encode authority strictly following RFC 3986. Fixes #191

* update signature vars to use "raw" prefix
  • Loading branch information
funkyshu authored Jun 18, 2024
1 parent 290d57b commit 61cd0b1
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .gvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
go1.22.0
go1.22.4
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]

## [6.14.3] - 2024-05-30
## [6.15.0] - 2024-06-18
### Added
- Fixed #191 - Add a util functions to encode authority strictly following RFC 3986.

## [6.14.3] - 2024-06-11
### Fixed
- Fixed #189 - Update utils authority package to handle proper encoding/decoding of uri with reserved characters.

Expand Down
64 changes: 64 additions & 0 deletions utils/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,67 @@ func validOptionalPort(port string) bool {
}
return true
}

// EncodeUserInfo takes an unencoded URI authority userinfo string and encodes it
func EncodeUserInfo(rawUserInfo string) string {
parts := strings.SplitN(rawUserInfo, ":", 2)
encodedParts := make([]string, len(parts))
for i, part := range parts {
encoded := url.QueryEscape(part)
decoded := strings.NewReplacer(
"%21", "!", "%24", "$", "%26", "&", "%27", "'",
"%28", "(", "%29", ")", "%2A", "*", "%2B", "+",
"%2C", ",", "%3B", ";", "%3D", "=",
).Replace(encoded)
encodedParts[i] = decoded
}
return strings.Join(encodedParts, ":")
}

// EncodeAuthority takes an unencoded URI authority string and encodes it
func EncodeAuthority(rawAuthority string) (string, error) {
var userInfo, hostPort string

// Split the authority into user info and hostPort
atIndex := strings.LastIndex(rawAuthority, "@")
if atIndex != -1 {
userInfo = rawAuthority[:atIndex]
hostPort = rawAuthority[atIndex+1:]
} else {
hostPort = rawAuthority
}

// Encode userInfo if present
if userInfo != "" {
userInfo = EncodeUserInfo(userInfo)
}

// Split host and port
var host, port string
hostPortSplit := strings.SplitN(hostPort, ":", 2)
if len(hostPortSplit) > 0 {
host = hostPortSplit[0]
}
if len(hostPortSplit) > 1 {
port = hostPortSplit[1]
}

// Encode host and port
encodedHost := url.QueryEscape(host)
var encodedPort string
if port != "" {
encodedPort = url.QueryEscape(port)
}

// Reconstruct the encoded authority string
var encodedAuthority string
if userInfo != "" {
encodedAuthority = userInfo + "@"
}
encodedAuthority += encodedHost
if encodedPort != "" {
encodedAuthority += ":" + encodedPort
}

return encodedAuthority, nil
}
76 changes: 75 additions & 1 deletion utils/authority_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type authorityTest struct {
}

func (a *authoritySuite) TestAuthority() {
tests := []*authorityTest{
tests := []authorityTest{
{
authorityString: "",
host: "",
Expand Down Expand Up @@ -308,6 +308,80 @@ func (a *authoritySuite) TestAuthority() {
}
}

type encodeAuthorityTest struct {
rawAuthority string
expectedEncoded string
hasError bool
errMessage string
message string
}

func (a *authoritySuite) TestEncodeAuthority() {
tests := []encodeAuthorityTest{
{
rawAuthority: "domain.com\\[email protected]:22",
expectedEncoded: "domain.com%[email protected]:22",
hasError: false,
errMessage: "",
message: "basic encoding",
},
{
rawAuthority: "example.com:80",
expectedEncoded: "example.com:80",
hasError: false,
errMessage: "",
message: "no user info",
},
{
rawAuthority: "user:[email protected]",
expectedEncoded: "user:[email protected]",
hasError: false,
errMessage: "",
message: "username and password",
},
{
rawAuthority: "[email protected]:22",
expectedEncoded: "[email protected]:22",
hasError: false,
errMessage: "",
message: "exclamation point in username (remains unencoded)",
},
{
rawAuthority: "[email protected]",
expectedEncoded: "[email protected]",
hasError: false,
errMessage: "",
message: "username only",
},
{
rawAuthority: "host.com:8080",
expectedEncoded: "host.com:8080",
hasError: false,
errMessage: "",
message: "host and port only",
},
{
rawAuthority: "@host.com",
expectedEncoded: "host.com",
hasError: false,
errMessage: "",
message: "empty user info",
},
}

for _, t := range tests {
a.Run(t.message, func() {
actual, err := EncodeAuthority(t.rawAuthority)
if t.hasError {
a.ErrorContains(err, t.errMessage, t.message)
} else {
a.NoError(err, t.message)
a.Equal(t.expectedEncoded, actual, t.message)
}
})
}
}

func TestAuthority(t *testing.T) {
suite.Run(t, new(authoritySuite))
}

0 comments on commit 61cd0b1

Please sign in to comment.