-
Notifications
You must be signed in to change notification settings - Fork 18.1k
net/http: ServeFile bug when directory address ends without "/" #13996
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
Comments
CC @bradfitz |
I don't really understand. What is "mydomain"? |
I'm sorry. The "mydomain" is exactly "http://localhost". I've edited it. I was confused before. It's just because the broswer don't know whether it's a directory or file. Broswer distinguish them by the "/" at the end of the URL. So a directory ended without "/" will be treated as a file by broswer. Here an example: view-source:localhost:3000/.vim <pre>
<a href=".netrwhist">.netrwhist</a>
<a href="autoload/">autoload/</a>
<a href="bundle/">bundle/</a>
<a href="vimrc">vimrc</a>
</pre> There is nothing wrong in href. But since "localhost:3000/.vim" is a file. The link generated by broswer is localhost:3000/autoload/, localhost:3000/bundle/ and localhost:3000/vimrc https://github.com/golang/go/blob/master/src/net/http/fs.go#L460 Expected to be serveFile(w, r, Dir(dir), file, true) here |
I'm asking about your expectation of the output, not your expectations of the code. What HTML is wrong, from your perspective? Browser distinguish what things are by the response Content-Type. There is no concept of a "directory" as far as browsers are concerned, except for the behavior of relative links. So perhaps this bug is about relative links not working? Sorry, I still don't understand. Please show me your server code, test files, what URL you hit, what HTML is produced, and what HTML you expect to be produced. |
There is nothing wrong in html output. They have same html output. But the links generated by broswer are different. Click the "testFile" |
https://github.com/golang/go/blob/master/src/net/http/fs.go#L353 The redirect flag here seems to be useless. A directory index flag will be better, I think. Directory index is sometimes annoying. To avoid it, I have to write about 20 extra lines: func Static(next http.Handler, staticPath string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
const indexPage = "/index.html"
path := staticPath + r.URL.Path[1:]
f, err := os.Stat(path)
if err == nil {
if f.IsDir() {
_, err := os.Stat(pathFix(path) + indexPage)
if err == nil {
http.ServeFile(w, r, path)
} else {
if next != nil {
next.ServeHTTP(w, r)
}
}
} else {
http.ServeFile(w, r, path)
}
} else {
if next != nil {
next.ServeHTTP(w, r)
}
}
}
return http.HandlerFunc(fn)
} |
@cirfi please show me your code and your expected behavior. it seems working good to me. |
Code: http://130.211.241.67:3000/test.go The following 2 URLs has the same html output. But as they have different endings, broswer generates different links. http://130.211.241.67:3000/test http://130.211.241.67:3000/test/ Just click "testFile" The ServeFile function doesn't redirect directory URLs ends without "/" to right place. If ServeFile redirects, there are no difference between ServeFile and FileServer. So I wonder how to modify it. |
@cirfi Ah, you mean "accessing directory which is not ended with last slash, it should return Location header". Right? |
@bradfitz it seems different behavior between: package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir(".")))
http.ListenAndServe(":3000", nil)
} and package main
import (
"net/http"
"path/filepath"
)
func serveFile(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filepath.Join(".", r.URL.Path))
}
func main() {
http.HandleFunc("/", serveFile)
http.ListenAndServe(":3000", nil)
} first one return |
@mattn func ServeFile(w ResponseWriter, r *Request, name string) {
dir, file := filepath.Split(name)
serveFile(w, r, Dir(dir), file, false) // <- redirect flag is set to be false
} FileServer: func FileServer(root FileSystem) Handler {
return &fileHandler{root}
}
func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
upath := r.URL.Path
if !strings.HasPrefix(upath, "/") {
upath = "/" + upath
r.URL.Path = upath
}
serveFile(w, r, f.root, path.Clean(upath), true) // <- redirect flag's set to be true
} Redirect part in serveFile if redirect {
// redirect to canonical path: / at end of directory url
// r.URL.Path always begins with /
url := r.URL.Path
if d.IsDir() {
if url[len(url)-1] != '/' {
localRedirect(w, r, path.Base(url)+"/")
return
}
} else {
if url[len(url)-1] == '/' {
localRedirect(w, r, "../"+path.Base(url))
return
}
}
} |
If I write a framework, it's ok for me to do such thing. Thankfully, I have nginx. |
It definitely does seem like a bug. Looking at the history, it seems like it's been like this since the day I joined the project May 5, 2010 (git rev 954ea53): Here's the code at that time: https://github.com/golang/go/blob/954ea53582eaff84a560b12ce880d084d7ea428f/src/pkg/http/fs.go // ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(c *Conn, r *Request, name string) {
serveFileInternal(c, r, name, false)
}
func serveFileInternal(c *Conn, r *Request, name string, redirect bool) {
const indexPage = "/index.html"
...
} It has always passed false there. I don't know why. I remember I didn't like ServeFile at the time, but not because of how it handled directories, but how it assumed the local disk (os.Open) was the source of all content. I worked to get ServeContent added in git rev 4539d1f (#2039), but I don't think I've ever looked at ServeFile. If I change ServeFile's false to true and re-run the net/http tests, there are failures:
I haven't looked into why. We can investigate in the Go 1.7 dev cycle. For my future self, to reproduce the original report: $ mkdir /tmp/foo func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filepath.Join("/tmp/foo", r.URL.Path[1:]))
})
log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil))
} |
@bradfitz |
CL https://golang.org/cl/20128 mentions this issue. |
FWIW, the fix introduced code duplication. As I was cleaning it up (as a first contribution), I looked up the commit history (well, I too was wondering what the I would've liked to suggest that the So let's dig into that Fixing the redirect loop would be as easy as checking whether |
go version 1.5.3
Suppose a directory Documents/ has sub-directories A/, B/, C/, and files a, b, c
URL in address bar: http://localhost/Documents/
Everything is okay
URL in address bar: http://localhost/Documents
Sub-directory's href is "http://localhost/A/", NOT "http://localhost/Documents/A/".
So do other sub-directories and files.
https://github.com/golang/go/blob/master/src/net/http/fs.go#L458-L461
Why is the redirect flag in serveFile(w, r, Dir(dir), file, false) set to be false?
What's the difference between ServeFile and FileServer except that FileServer will add "/" for directories and remove "/" for files?
As suggestion, I hope that ServeFile won't show directory structure. It only serves files.
Edited
Code: http://130.211.241.67:3000/test.go
The following 2 URLs has the same html output. But as two URLs have different endings, broswer generates different links.
http://130.211.241.67:3000/test
http://130.211.241.67:3000/test/
Just click "testFile"
The ServeFile function doesn't redirect directory URLs ends without "/" to right place.
If ServeFile redirects, there are no difference between ServeFile and FileServer.
@bradfitz tested
#13996 (comment)
So I wonder how to modify it.
Edited Jan 20, 2016, 13:40:00 UTC+8
@bradfitz 's test
#13996 (comment)
The text was updated successfully, but these errors were encountered: