Skip to content

net/url: url.String() produces invalid relative URL when first segment of path contains ":" #17184

@vcabbage

Description

@vcabbage

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

go version go1.7.1 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/user/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.7.1/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.7.1/libexec/pkg/tool/darwin_amd64"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/jv/fp0q8rhx427f6p892mh4mpx40000gn/T/go-build154120093=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"

What did you do?

Created a simple file server using http.FileServer to serve a directory containing a file with a colon (":") in the name.

package main

import "net/http"

func main() {
    http.Handle("/", http.FileServer(http.Dir("files")))
    http.ListenAndServe("127.0.0.1:8000", nil)
}
# ls -1 files
normal_file
weird:file

What did you expect to see?

On browsing to 127.0.0.1:8000 I expected a list of the two files shown above and that I would be able to retrieve their contents by clicking their respective links.

What did you see instead?

I was able to retrieve normal_file, but selecting weird:file appeared to do nothing. Viewing the source, it became apparent that the browser was interpreting weird: as the scheme.

<pre>
<a href="normal_file">normal_file</a>
<a href="weird:file">weird:file</a>
</pre>

Per RFC 3986, Section 4.2:

A path segment that contains a colon character (e.g., "this:that")
cannot be used as the first segment of a relative-path reference, as
it would be mistaken for a scheme name. Such a segment must be
preceded by a dot-segment (e.g., "./this:that") to make a relative-
path reference.

I think the correct place to resolve the behavior is in URL.String(). It's comment states String reassembles the URL into a valid URL string., which is not accurate in this case of only a relative path with a colon in the first segment.

I'd be happy to contribute a fix. I believe adding the below before writing the path would correct the behavior for the specific case, without affecting the output otherwise.

        if buf.Len() == 0 {
            cIdx, sIdx := strings.IndexByte(path, ':'), strings.IndexByte(path, '/')
            if cIdx > -1 && (sIdx < 0 || cIdx < sIdx) {
                buf.WriteString("./")
            }
        }

This produces:

<pre>
<a href="normal_file">normal_file</a>
<a href="./weird:file">weird:file</a>
</pre>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions