Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/url: Path and RawPath are hard to use #17340

Closed
theory opened this issue Oct 4, 2016 · 2 comments
Closed

net/url: Path and RawPath are hard to use #17340

theory opened this issue Oct 4, 2016 · 2 comments

Comments

@theory
Copy link

theory commented Oct 4, 2016

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.6 darwin/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/david/dev/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"

What did you do?

I wanted to create a URL where a path segment had an encoded slash. This is because usernames in our system are allowed to contain any valid UTF-8 character, and usernames are used in URLs. But getting the url package to properly encode the slash is tricky. Sample code:

username := "foo/bar"
url := url.URL{
    Scheme: "https",
    Host:   "example.com",
    Path:   "/user/" + username,
}
println(url.String())

What did you expect to see?

https://example.com/user/foo%2Fbar

What did you see instead?

https://example.com/user/foo/bar

Of course I understand that it's impossible for this example to know that "foo/bar" is a single path segment, instead of two. But I tried also supplying a raw path:

username := "foo/bar"
url := url.URL{
    Scheme:  "https",
    Host:    "example.com",
    Path:    "/user/" + username,
    RawPath: "user/" + strings.Replace(username, "/", "%2F", -1),
}
println(url.String())

But the output was the same. The only way I've been able to figure out how to get it to know that "foo/bar" is a single segment is by manually encoding each segment myself and then having the url package parse it:

username := "foo/bar"
raw := "https://example.com/user/" + strings.Replace(
    (&url.URL{Path: username}).String(),
    "/", "%2F", -1,
)

url, err := url.Parse(raw)
if err != nil {
    panic(err)
}
println(url.String())

This outputs the correct value:

https://example.com/user/foo%2Fbar

However, since the whole point of constructing a URL from its parts it to generate a string representation (e.g., to pass to http.Get()), this kind of defeats the whole purpose: I can simply use the raw variable.

I'd like a way to be able to specify the path as a slice of segments, so that the url package knows which parts are directory segments and which aren't. Something like:

url := url.URL{
    Scheme:  "https",
    Host:    "example.com",
    Segments: []string{"user", username}
}
@quentinmit quentinmit changed the title Creating URLs with Encoded / in the Path is Inconvenient net/url: Path and RawPath are hard to use Oct 4, 2016
@quentinmit
Copy link
Contributor

You are on the right track with RawPath, but your Path and RawPath do not match:

    Path:    "/user/" + username,
    RawPath: "user/" + strings.Replace(username, "/", "%2F", -1),

This yields a Path of "/user/foo/bar" and a RawPath of "user/foo%2Fbar". The documentation for URL says:

The Parse function sets both Path and RawPath in the URL it returns, and URL's String method uses RawPath if it is a valid encoding of Path, by calling the EscapedPath method.

In this case, your RawPath is missing the leading slash, and therefore it is not a valid encoding of Path. This should just work correctly if you add the missing slash.

That said, you should probably also be using url.QueryEscape, instead of your strings.Replace, because there might be other characters to escape as well.

We do not use our issue tracker for questions; please see the resources at https://golang.org/wiki/Questions for help debugging code.

@theory
Copy link
Author

theory commented Oct 4, 2016

Lack of the leading / was a typo, but you're right, it does work properly with it; not sure why I couldn't get it to work yesterday; perhaps I had a similar bug then. This works:

username := "foo/bar"
url := url.URL{
    Scheme:  "https",
    Host:    "example.com",
    Path:    "/user/" + username,
    RawPath: "/user/" + url.QueryEscape(username),
}
println(url.String())

Still, I wasn't asking a question (though I'm glad to have this improved workaround), but requesting a feature: The ability to specify the path as a slice of segments. This allows me to avoid knowing what to escape (RawPath) and what not to (Path), which always feels weird, like more cognitive overhead than really ought to be necessary.

@golang golang locked and limited conversation to collaborators Oct 4, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants