5

When I implement a static server handler, if I access to root path, it will show the whole directory, like this:

access to root folder

My code is:

package main

import (
    "flag"
    "log"
    "net/http"
    "strings"
)

func main() {
    port := flag.String("p", "3000", "port to serve on")
    directory := flag.String("d", ".", "the directory of static file to host")
    flag.Parse()

    http.Handle("/statics/", http.StripPrefix(strings.TrimRight("/statics/", "/"), http.FileServer(http.Dir(*directory))))

    log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
    log.Fatal(http.ListenAndServe(":"+*port, nil))
}

go to : http://locahost:3000/statics/

Hau Ma
  • 254
  • 2
  • 14
  • Perhaps this is what you are looking for? The resulting answer appers somewhat dirty to me, however. It basically consists of just checking if the currently requested resource is a Dir, or a File and handling it accordingly. https://stackoverflow.com/questions/49589685/good-way-to-disable-directory-listing-with-http-fileserver-in-go – RayfenWindspear Jul 04 '18 at 09:08
  • Uuuh, yes, this is what `Dir` does: [A Dir implements FileSystem using the native file system restricted to a specific directory tree](https://golang.org/pkg/net/http/#Dir) What is the problem exactly? – RickyA Jul 04 '18 at 09:19
  • 1
    @RickyA the problem is that most other servers default to returning a 404 when hitting a directory, which is a very sane default for security. Go doesn't offer a way to turn this off. Essentially the question's answer boils down to the last piece of the doc for `Dir`: "create a custom FileSystem implementation." – RayfenWindspear Jul 04 '18 at 09:22
  • @RayfenWindspear that is an assumption. Maybe OP expects an index.html to be rendered, so I want to hear it from OP. – RickyA Jul 04 '18 at 09:25
  • 1
    @RickyA not to seem rude, but it seems a safe assumption. Serving a "statics" dir with css, js, etc wouldn't normally have an index.html at the `statics/` level of the dir as it is just a container for the separate types of statics. But yes, an assumption nonetheless... – RayfenWindspear Jul 04 '18 at 09:27
  • Thank you so much guys, I implemented a solution to my own repo related to the correct answer: https://github.com/hauxe/GoM/blob/master/http/filesystem_handler.go – Hau Ma Jul 04 '18 at 09:47

1 Answers1

9

Fortunately or not, the default behavior of http.FileServer is that if the path denotes a directory without an index.html file, a directory listing will be served. And it does not provide a simple way to turn that off. But...

http.FileServer operates on a virtual filesystem described by the http.FileSystem interface.

This interface has a single method which tells how to open a file, and obtain an http.File "view" of it:

type FileSystem interface {
        Open(name string) (File, error)
}

If you want to disable directory listing, all you have to do is provide your own implementation of http.FileSystem which when a directory is targeted, you simply report / return an error. That's all it takes.

Of course you don't have to do this all by yourself. You may create your own FileSystem which uses / utilizes http.Dir, which is the default implementation that uses the native file system (restricted to a specific directory tree).

type myfs struct {
    http.Dir
}

func (m myfs) Open(name string) (result http.File, err error) {
    f, err := m.Dir.Open(name)
    if err != nil {
        return
    }

    fi, err := f.Stat()
    if err != nil {
        return
    }
    if fi.IsDir() {
        // Return a response that would have been if directory would not exist:
        return m.Dir.Open("does-not-exist")
    }
    return f, nil
}

Using the above custom implementation:

handler := http.FileServer(myfs{http.Dir(*directory)})
http.Handle(
    "/statics/",
    http.StripPrefix(strings.TrimRight("/statics/", "/"), handler),
)

And that's all. Attempting to browse http://locahost:3000/statics/ will result in the default response:

404 page not found

Notes:

The above implementation does a second Dir.Open() call to return an error, which is always the same. To "speed things up", we can store this response and just reuse it:

var notFoundFile, notFoundErr = http.Dir("dummy").Open("does-not-exist")

And when we detect a directory in our myfs.Open() method:

if fi.IsDir() {
    // Return a response that "belogns" to opening a non-existing folder:
    return notFoundFile, notFoundErr
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • 3
    +1 Straight to the point. Still seems odd that such a basic piece of functionality isn't builtin as an option. – RayfenWindspear Jul 04 '18 at 09:43
  • 1
    @RayfenWindspear I agree, `http.FileServer` could use a little customization, in the public's best interest... – icza Jul 04 '18 at 09:48