Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good way to disable directory listing with http.FileServer in Go

If you use the http.FileServer in Go like:

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

    http.Handle("/", http.FileServer(http.Dir(*directory)))

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

Then accessing a directory will give you a listing of files. Often this is disabled for web services and instead responds with 404 and I would like this behaviour too.

http.FileServer has no options for this AFAIK and I have seen a proposed way to solve this here https://groups.google.com/forum/#!topic/golang-nuts/bStLPdIVM6w what they do is wrapping the http.FileSystem type and implementing an own Open method. However this doesn't give a 404 when the path is a directory, it just gives a blank page, and it's unclear how to modify it to accomodate this. This is what they do:

type justFilesFilesystem struct {
    fs http.FileSystem
}

func (fs justFilesFilesystem) Open(name string) (http.File, error) {
    f, err := fs.fs.Open(name)
    if err != nil {
        return nil, err
    }
    return neuteredReaddirFile{f}, nil
}

type neuteredReaddirFile struct {
    http.File
}

func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
    return nil, nil
}

func main() {
    fs := justFilesFilesystem{http.Dir("/tmp/")}
    http.ListenAndServe(":8080", http.FileServer(fs))
}

Note: if you make Readdir return nil, os.ErrNotExist then you get a 500 response with "Error reading directory" - not 404.

Any ideas on how to neatly present a 404 and still preserving the feature of automatically finding an index.html if present?

like image 929
Tobias Avatar asked Mar 31 '18 15:03

Tobias


1 Answers

This behavior can be changed if you substitute not a Readdir method, but the Stat.
Please take a look at working code below. It supports serving of index.html files if they are inside of requested directory and returns 404 in case there is no index.html and it is a directory.

    package main

    import (
        "io"
        "net/http"
        "os"
    )

    type justFilesFilesystem struct {
        fs               http.FileSystem
        // readDirBatchSize - configuration parameter for `Readdir` func  
        readDirBatchSize int
    }

    func (fs justFilesFilesystem) Open(name string) (http.File, error) {
        f, err := fs.fs.Open(name)
        if err != nil {
            return nil, err
        }
        return neuteredStatFile{File: f, readDirBatchSize: fs.readDirBatchSize}, nil
    }

    type neuteredStatFile struct {
        http.File
        readDirBatchSize int
    }

    func (e neuteredStatFile) Stat() (os.FileInfo, error) {
        s, err := e.File.Stat()
        if err != nil {
            return nil, err
        }
        if s.IsDir() {
        LOOP:
            for {
                fl, err := e.File.Readdir(e.readDirBatchSize)
                switch err {
                case io.EOF:
                    break LOOP
                case nil:
                    for _, f := range fl {
                        if f.Name() == "index.html" {
                            return s, err
                        }
                    }
                default:
                    return nil, err
                }
            }
            return nil, os.ErrNotExist
        }
        return s, err
    }

    func main() {
        fs := justFilesFilesystem{fs: http.Dir("/tmp/"), readDirBatchSize: 2}
        fss := http.FileServer(fs)
        http.ListenAndServe(":8080", fss)
    }
like image 125
Pavlo Strokov Avatar answered Oct 02 '22 14:10

Pavlo Strokov