0

router.PathPrefix("/test/").Handler(http.StripPrefix("/test/", http.FileServer(http.Dir("/assets/"))))

In this example, the root directory of the file server is set to /assets/. My goal is to set this root directory based on the cookie in the HTTP request. I know I am able to do something like this:

type AlternativeFileServer struct { }

func AlternativeFileServerFactory() http.Handler {
    return AlternativeFileServer{}
}

func (aFs AlternativeFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    cookie, err := GetCookie(r)
    if err != nil {
        // handle error 
    }

    var rootDirectory string
    if cookie == "x" {
        rootDirectory = "assets"
    } else {
        rootDirectory = "alternative"
    }

    path := filepath.join(rootDirectory, r.URL.Path) + ".png"

    http.ServeFile(path) 
}

func main() {
    ....
    router.PathPrefix("/test/").Handler(http.StripPrefix("/test/", AlternativeFileServerFactory())
}

But I was hoping there was a better alternative where I could wrap the http.FileServer directly and dynamically set its root directory.

Is that possible?

Flimzy
  • 68,325
  • 15
  • 126
  • 165
SSS
  • 691
  • 2
  • 8
  • 18
  • I see nothing wrong with your approach as implemented (except for the naming of the object and its constructor function, but this is a question of personal taste). – kostix Jun 23 '20 at 12:30
  • https://stackoverflow.com/questions/28793619/golang-what-to-use-http-servefile-or-http-fileserver - I understood from this question that attempting to implement a file server was potentially dangerous because if done incorrectly there are ways of breaking out of the root directory. Also (but less important) I thought this could be done with less code. That is why I was hesitant to use my current method. – SSS Jun 23 '20 at 12:42
  • I see, but you could opt to merely have your own file-server to aggregate two handlers returned by two calls to `http.FileServer` with different roots and use no-brainer dispatch to select either of them and call `ServeHTTP` on the selected one—with the parameters passed to the enclosing handler. – kostix Jun 23 '20 at 12:50

1 Answers1

1

One approach based on the comment thread:

type AltFileServer struct {
  assertsFS http.Handler
  altFS     http.Handler
}

func NewAltFileServer() http.Handler {
    return &AlternativeFileServer{
        assetsFS: http.FileServer("assets"),
        altFS:    http.FileServer("alternative"),
    }
}

func (fs *AltFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    cookie, err := GetCookie(r)
    if err != nil {
        // handle error 
    }

    if cookie == "x" {
        fs.assetsFS.ServeHTTP(w, r)
        return
    }

    fs.altFS.ServeHTTP(w, r) 
}

func main() {
    ....
    router.PathPrefix("/test/").Handler(http.StripPrefix("/test/", NewAltFileServer())
}
kostix
  • 47,535
  • 12
  • 80
  • 157