diff --git a/config.go b/config.go index d4a0f0e2..d9ff624c 100644 --- a/config.go +++ b/config.go @@ -523,18 +523,19 @@ func ParseConfig() (err error) { if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" { extensions := strings.Split(sgz, ",") - if len(extensions) > 0 { - StaticExtensionsToGzip = []string{} - for _, ext := range extensions { - if len(ext) == 0 { - continue - } - extWithDot := ext - if extWithDot[:1] != "." { - extWithDot = "." + extWithDot - } - StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot) + fileExts := []string{} + for _, ext := range extensions { + ext = strings.TrimSpace(ext) + if ext == "" { + continue } + if !strings.HasPrefix(ext, ".") { + ext = "." + ext + } + fileExts = append(fileExts, ext) + } + if len(fileExts) > 0 { + StaticExtensionsToGzip = fileExts } } diff --git a/context/output.go b/context/output.go index 743beb96..a4bb3d09 100644 --- a/context/output.go +++ b/context/output.go @@ -103,22 +103,10 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface //fix cookie not work in IE if len(others) > 0 { switch v := others[0].(type) { - case int: + case int, int32, int64: if v > 0 { fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v)*time.Second).UTC().Format(time.RFC1123), v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int64: - if v > 0 { - fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v)*time.Second).UTC().Format(time.RFC1123), v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int32: - if v > 0 { - fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v)*time.Second).UTC().Format(time.RFC1123), v) - } else if v < 0 { + } else if v <= 0 { fmt.Fprintf(&b, "; Max-Age=0") } } diff --git a/memzipfile.go b/memzipfile.go index 0fff44b6..b61e87f2 100644 --- a/memzipfile.go +++ b/memzipfile.go @@ -29,72 +29,72 @@ import ( ) var ( - gmfim = make(map[string]*memFileInfo) - lock sync.RWMutex + menFileInfoMap = make(map[string]*memFileInfo) + lock sync.RWMutex ) -// OpenMemZipFile returns MemFile object with a compressed static file. +// openMemZipFile returns MemFile object with a compressed static file. // it's used for serve static file if gzip enable. func openMemZipFile(path string, zip string) (*memFile, error) { - osfile, e := os.Open(path) + osFile, e := os.Open(path) if e != nil { return nil, e } - defer osfile.Close() + defer osFile.Close() - osfileinfo, e := osfile.Stat() + osFileInfo, e := osFile.Stat() if e != nil { return nil, e } - modtime := osfileinfo.ModTime() - fileSize := osfileinfo.Size() + modTime := osFileInfo.ModTime() + fileSize := osFileInfo.Size() lock.RLock() - cfi, ok := gmfim[zip+":"+path] + cfi, ok := menFileInfoMap[zip+":"+path] lock.RUnlock() - if !(ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize) { + if !(ok && cfi.ModTime() == modTime && cfi.fileSize == fileSize) { var content []byte if zip == "gzip" { - var zipbuf bytes.Buffer - gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression) + var zipBuf bytes.Buffer + gzipWriter, e := gzip.NewWriterLevel(&zipBuf, gzip.BestCompression) if e != nil { return nil, e } - _, e = io.Copy(gzipwriter, osfile) - gzipwriter.Close() + _, e = io.Copy(gzipWriter, osFile) + gzipWriter.Close() if e != nil { return nil, e } - content, e = ioutil.ReadAll(&zipbuf) + content, e = ioutil.ReadAll(&zipBuf) if e != nil { return nil, e } } else if zip == "deflate" { - var zipbuf bytes.Buffer - deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression) + var zipBuf bytes.Buffer + deflateWriter, e := flate.NewWriter(&zipBuf, flate.BestCompression) if e != nil { return nil, e } - _, e = io.Copy(deflatewriter, osfile) - deflatewriter.Close() + _, e = io.Copy(deflateWriter, osFile) + deflateWriter.Close() if e != nil { return nil, e } - content, e = ioutil.ReadAll(&zipbuf) + content, e = ioutil.ReadAll(&zipBuf) if e != nil { return nil, e } } else { - content, e = ioutil.ReadAll(osfile) + content, e = ioutil.ReadAll(osFile) if e != nil { return nil, e } } - cfi = &memFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize} + cfi = &memFileInfo{osFileInfo, modTime, content, int64(len(content)), fileSize} lock.Lock() defer lock.Unlock() - gmfim[zip+":"+path] = cfi + menFileInfoMap[zip+":"+path] = cfi } return &memFile{fi: cfi, offset: 0}, nil } @@ -139,7 +139,7 @@ func (fi *memFileInfo) Sys() interface{} { return nil } -// MemFile contains MemFileInfo and bytes offset when reading. +// memFile contains MemFileInfo and bytes offset when reading. // it implements io.Reader,io.ReadCloser and io.Seeker. type memFile struct { fi *memFileInfo @@ -198,7 +198,7 @@ func (f *memFile) Seek(offset int64, whence int) (ret int64, err error) { return f.offset, nil } -// GetAcceptEncodingZip returns accept encoding format in http header. +// getAcceptEncodingZip returns accept encoding format in http header. // zip is first, then deflate if both accepted. // If no accepted, return empty string. func getAcceptEncodingZip(r *http.Request) string { diff --git a/staticfile.go b/staticfile.go index fa8a56d4..de530b0c 100644 --- a/staticfile.go +++ b/staticfile.go @@ -31,97 +31,107 @@ func serverStaticRouter(ctx *context.Context) { return } requestPath := filepath.Clean(ctx.Input.Request.URL.Path) - i := 0 - for prefix, staticDir := range StaticDir { - if len(prefix) == 0 { - continue + + // special processing : favicon.ico/robots.txt can be in any static dir + if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { + file := path.Join(".", requestPath) + if utils.FileExists(file) { + http.ServeFile(ctx.ResponseWriter, ctx.Request, file) + return } - if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { + + for _, staticDir := range StaticDir { file := path.Join(staticDir, requestPath) if utils.FileExists(file) { http.ServeFile(ctx.ResponseWriter, ctx.Request, file) return } - i++ - if i == len(StaticDir) { - http.NotFound(ctx.ResponseWriter, ctx.Request) - return - } + } + + http.NotFound(ctx.ResponseWriter, ctx.Request) + return + } + + for prefix, staticDir := range StaticDir { + if len(prefix) == 0 { continue } if strings.HasPrefix(requestPath, prefix) { if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { continue } - file := path.Join(staticDir, requestPath[len(prefix):]) - finfo, err := os.Stat(file) + filePath := path.Join(staticDir, requestPath[len(prefix):]) + fileInfo, err := os.Stat(filePath) if err != nil { if RunMode == "dev" { - Warn("Can't find the file:", file, err) + Warn("Can't find the file:", filePath, err) } http.NotFound(ctx.ResponseWriter, ctx.Request) return } //if the request is dir and DirectoryIndex is false then - if finfo.IsDir() { + if fileInfo.IsDir() { if !DirectoryIndex { exception("403", ctx) return - } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { + } + if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) return } - } else if strings.HasSuffix(requestPath, "/index.html") { - file := path.Join(staticDir, requestPath) - if utils.FileExists(file) { - oFile, err := os.Open(file) - if err != nil { - if RunMode == "dev" { - Warn("Can't open the file:", file, err) - } - http.NotFound(ctx.ResponseWriter, ctx.Request) - } - defer oFile.Close() - http.ServeContent(ctx.ResponseWriter, ctx.Request, file, finfo.ModTime(), oFile) - return - } } - //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request - isStaticFileToCompress := false - if StaticExtensionsToGzip != nil && len(StaticExtensionsToGzip) > 0 { - for _, statExtension := range StaticExtensionsToGzip { - if strings.HasSuffix(strings.ToLower(file), strings.ToLower(statExtension)) { - isStaticFileToCompress = true - break - } - } - } - - if isStaticFileToCompress { - var contentEncoding string - if EnableGzip { - contentEncoding = getAcceptEncodingZip(ctx.Request) - } - - memzipfile, err := openMemZipFile(file, contentEncoding) + if strings.HasSuffix(requestPath, "/index.html") { + fileReader, err := os.Open(filePath) if err != nil { + if RunMode == "dev" { + Warn("Can't open the file:", filePath, err) + } + http.NotFound(ctx.ResponseWriter, ctx.Request) return } - - if contentEncoding == "gzip" { - ctx.Output.Header("Content-Encoding", "gzip") - } else if contentEncoding == "deflate" { - ctx.Output.Header("Content-Encoding", "deflate") - } else { - ctx.Output.Header("Content-Length", strconv.FormatInt(finfo.Size(), 10)) - } - - http.ServeContent(ctx.ResponseWriter, ctx.Request, file, finfo.ModTime(), memzipfile) - - } else { - http.ServeFile(ctx.ResponseWriter, ctx.Request, file) + defer fileReader.Close() + http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, fileInfo.ModTime(), fileReader) + return } + + isStaticFileToCompress := false + for _, statExtension := range StaticExtensionsToGzip { + if strings.HasSuffix(strings.ToLower(filePath), strings.ToLower(statExtension)) { + isStaticFileToCompress = true + break + } + } + + if !isStaticFileToCompress { + http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath) + return + } + + //to compress file + var contentEncoding string + if EnableGzip { + contentEncoding = getAcceptEncodingZip(ctx.Request) + } + + memZipFile, err := openMemZipFile(filePath, contentEncoding) + if err != nil { + if RunMode == "dev" { + Warn("Can't compress the file:", filePath, err) + } + http.NotFound(ctx.ResponseWriter, ctx.Request) + return + } + + if contentEncoding == "gzip" { + ctx.Output.Header("Content-Encoding", "gzip") + } else if contentEncoding == "deflate" { + ctx.Output.Header("Content-Encoding", "deflate") + } else { + ctx.Output.Header("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)) + } + + http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, fileInfo.ModTime(), memZipFile) return } }