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?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
SSS
  • 761
  • 2
  • 9
  • 19
  • 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
  • 51,517
  • 14
  • 93
  • 176