Last active
August 29, 2015 14:21
-
-
Save klauspost/baad923e77caf20b777e to your computer and use it in GitHub Desktop.
revel static file server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package controllers | |
import ( | |
"os" | |
fpath "path/filepath" | |
"strings" | |
"syscall" | |
"github.com/revel/revel" | |
) | |
type MyStatic struct { | |
*revel.Controller | |
} | |
type DevStatic struct { | |
MyStatic | |
} | |
// This method handles requests for files. The supplied prefix may be absolute | |
// or relative. If the prefix is relative it is assumed to be relative to the | |
// application directory. The filepath may either be just a file or an | |
// additional filepath to search for the given file. This response may return | |
// the following responses in the event of an error or invalid request; | |
// 403(Forbidden): If the prefix filepath combination results in a directory. | |
// 404(Not found): If the prefix and filepath combination results in a non-existent file. | |
// 500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel. | |
// | |
// Note that when defining routes in routes/conf the parameters must not have | |
// spaces around the comma. | |
// Bad: Static.Serve("public/img", "favicon.png") | |
// Good: Static.Serve("public/img","favicon.png") | |
// | |
// Examples: | |
// Serving a directory | |
// Route (conf/routes): | |
// GET /public/{<.*>filepath} Static.Serve("public") | |
// Request: | |
// public/js/sessvars.js | |
// Calls | |
// Static.Serve("public","js/sessvars.js") | |
// | |
// Serving a file | |
// Route (conf/routes): | |
// GET /favicon.ico Static.Serve("public/img","favicon.png") | |
// Request: | |
// favicon.ico | |
// Calls: | |
// Static.Serve("public/img", "favicon.png") | |
func (c MyStatic) Serve(prefix, filepath string) revel.Result { | |
var basePath string | |
if !fpath.IsAbs(prefix) { | |
basePath = revel.BasePath | |
} | |
basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix)) | |
fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath)) | |
if !strings.HasPrefix(fname, basePathPrefix) { | |
revel.WARN.Printf("Attempted to read file outside of base path: %s", fname) | |
return c.NotFound("") | |
} | |
displayName := fpath.Base(fname) | |
c.Response.Out.Header().Set("Content-Type", revel.ContentTypeByFilename(fname)) | |
enc := c.Request.Header.Get("Accept-Encoding") | |
gzallow := strings.Contains(strings.ToLower(enc), "gzip") | |
finfo, err := os.Stat(fname + ".gz") | |
if gzallow && err == nil && !finfo.Mode().IsDir() { | |
fname = fname + ".gz" | |
c.Response.Out.Header().Set("Content-Encoding", "gzip") | |
} else { | |
finfo, err = os.Stat(fname) | |
if err != nil { | |
if os.IsNotExist(err) || err.(*os.PathError).Err == syscall.ENOTDIR { | |
revel.WARN.Printf("File not found (%s): %s ", fname, err) | |
return c.NotFound("File not found") | |
} | |
revel.ERROR.Printf("Error trying to get fileinfo for '%s': %s", fname, err) | |
return c.RenderError(err) | |
} | |
if finfo.Mode().IsDir() { | |
revel.WARN.Printf("Attempted directory listing of %s", fname) | |
return c.Forbidden("Directory listing not allowed") | |
} | |
} | |
file, err := os.Open(fname) | |
if err != nil { | |
if os.IsNotExist(err) { | |
revel.WARN.Printf("File not found (%s): %s ", fname, err) | |
return c.NotFound("File not found") | |
} | |
revel.ERROR.Printf("Error opening '%s': %s", fname, err) | |
return c.RenderError(err) | |
} | |
c.Response.Out.Header().Set("Cache-Control", "no-transform,public,max-age=3000,s-maxage=9000") | |
return c.RenderBinary(file, displayName, revel.Inline, finfo.ModTime()) | |
} | |
// This method allows modules to serve binary files. The parameters are the same | |
// as Static.Serve with the additional module name pre-pended to the list of | |
// arguments. | |
func (c MyStatic) ServeModule(moduleName, prefix, filepath string) revel.Result { | |
var basePath string | |
for _, module := range revel.Modules { | |
if module.Name == moduleName { | |
basePath = module.Path | |
} | |
} | |
absPath := fpath.Join(basePath, fpath.FromSlash(prefix)) | |
return c.Serve(absPath, filepath) | |
} | |
func (c DevStatic) Serve(prefix, filepath string) revel.Result { | |
if revel.DevMode { | |
return c.MyStatic.Serve(prefix, filepath) | |
} | |
return c.NotFound("not found") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment