1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-22 08:00:57 +00:00

beego: move staticServer to New file

This commit is contained in:
astaxie 2014-04-06 00:18:21 +08:00
parent 73d757e3f4
commit 3255a43568
3 changed files with 126 additions and 141 deletions

View File

@ -14,12 +14,12 @@ import (
"time" "time"
) )
var gmfim map[string]*MemFileInfo = make(map[string]*MemFileInfo) var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo)
var lock sync.RWMutex var 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. // it's used for serve static file if gzip enable.
func OpenMemZipFile(path string, zip string) (*MemFile, error) { func openMemZipFile(path string, zip string) (*memFile, error) {
osfile, e := os.Open(path) osfile, e := os.Open(path)
if e != nil { if e != nil {
return nil, e return nil, e
@ -36,12 +36,9 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
lock.RLock() lock.RLock()
cfi, ok := gmfim[zip+":"+path] cfi, ok := gmfim[zip+":"+path]
lock.RUnlock() lock.RUnlock()
if ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize { if !(ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize) {
} else {
var content []byte var content []byte
if zip == "gzip" { if zip == "gzip" {
//将文件内容压缩到zipbuf中
var zipbuf bytes.Buffer var zipbuf bytes.Buffer
gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression) gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression)
if e != nil { if e != nil {
@ -52,13 +49,11 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
if e != nil { if e != nil {
return nil, e return nil, e
} }
//读zipbuf到content
content, e = ioutil.ReadAll(&zipbuf) content, e = ioutil.ReadAll(&zipbuf)
if e != nil { if e != nil {
return nil, e return nil, e
} }
} else if zip == "deflate" { } else if zip == "deflate" {
//将文件内容压缩到zipbuf中
var zipbuf bytes.Buffer var zipbuf bytes.Buffer
deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression) deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression)
if e != nil { if e != nil {
@ -69,7 +64,6 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
if e != nil { if e != nil {
return nil, e return nil, e
} }
//将zipbuf读入到content
content, e = ioutil.ReadAll(&zipbuf) content, e = ioutil.ReadAll(&zipbuf)
if e != nil { if e != nil {
return nil, e return nil, e
@ -81,17 +75,17 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
} }
} }
cfi = &MemFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize} cfi = &memFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize}
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
gmfim[zip+":"+path] = cfi gmfim[zip+":"+path] = cfi
} }
return &MemFile{fi: cfi, offset: 0}, nil return &memFile{fi: cfi, offset: 0}, nil
} }
// MemFileInfo contains a compressed file bytes and file information. // MemFileInfo contains a compressed file bytes and file information.
// it implements os.FileInfo interface. // it implements os.FileInfo interface.
type MemFileInfo struct { type memFileInfo struct {
os.FileInfo os.FileInfo
modTime time.Time modTime time.Time
content []byte content []byte
@ -100,62 +94,62 @@ type MemFileInfo struct {
} }
// Name returns the compressed filename. // Name returns the compressed filename.
func (fi *MemFileInfo) Name() string { func (fi *memFileInfo) Name() string {
return fi.Name() return fi.Name()
} }
// Size returns the raw file content size, not compressed size. // Size returns the raw file content size, not compressed size.
func (fi *MemFileInfo) Size() int64 { func (fi *memFileInfo) Size() int64 {
return fi.contentSize return fi.contentSize
} }
// Mode returns file mode. // Mode returns file mode.
func (fi *MemFileInfo) Mode() os.FileMode { func (fi *memFileInfo) Mode() os.FileMode {
return fi.Mode() return fi.Mode()
} }
// ModTime returns the last modified time of raw file. // ModTime returns the last modified time of raw file.
func (fi *MemFileInfo) ModTime() time.Time { func (fi *memFileInfo) ModTime() time.Time {
return fi.modTime return fi.modTime
} }
// IsDir returns the compressing file is a directory or not. // IsDir returns the compressing file is a directory or not.
func (fi *MemFileInfo) IsDir() bool { func (fi *memFileInfo) IsDir() bool {
return fi.IsDir() return fi.IsDir()
} }
// return nil. implement the os.FileInfo interface method. // return nil. implement the os.FileInfo interface method.
func (fi *MemFileInfo) Sys() interface{} { func (fi *memFileInfo) Sys() interface{} {
return nil 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. // it implements io.Reader,io.ReadCloser and io.Seeker.
type MemFile struct { type memFile struct {
fi *MemFileInfo fi *memFileInfo
offset int64 offset int64
} }
// Close memfile. // Close memfile.
func (f *MemFile) Close() error { func (f *memFile) Close() error {
return nil return nil
} }
// Get os.FileInfo of memfile. // Get os.FileInfo of memfile.
func (f *MemFile) Stat() (os.FileInfo, error) { func (f *memFile) Stat() (os.FileInfo, error) {
return f.fi, nil return f.fi, nil
} }
// read os.FileInfo of files in directory of memfile. // read os.FileInfo of files in directory of memfile.
// it returns empty slice. // it returns empty slice.
func (f *MemFile) Readdir(count int) ([]os.FileInfo, error) { func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
infos := []os.FileInfo{} infos := []os.FileInfo{}
return infos, nil return infos, nil
} }
// Read bytes from the compressed file bytes. // Read bytes from the compressed file bytes.
func (f *MemFile) Read(p []byte) (n int, err error) { func (f *memFile) Read(p []byte) (n int, err error) {
if len(f.fi.content)-int(f.offset) >= len(p) { if len(f.fi.content)-int(f.offset) >= len(p) {
n = len(p) n = len(p)
} else { } else {
@ -171,7 +165,7 @@ var errWhence = errors.New("Seek: invalid whence")
var errOffset = errors.New("Seek: invalid offset") var errOffset = errors.New("Seek: invalid offset")
// Read bytes from the compressed file bytes by seeker. // Read bytes from the compressed file bytes by seeker.
func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) { func (f *memFile) Seek(offset int64, whence int) (ret int64, err error) {
switch whence { switch whence {
default: default:
return 0, errWhence return 0, errWhence
@ -191,7 +185,7 @@ func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) {
// GetAcceptEncodingZip returns accept encoding format in http header. // GetAcceptEncodingZip returns accept encoding format in http header.
// zip is first, then deflate if both accepted. // zip is first, then deflate if both accepted.
// If no accepted, return empty string. // If no accepted, return empty string.
func GetAcceptEncodingZip(r *http.Request) string { func getAcceptEncodingZip(r *http.Request) string {
ss := r.Header.Get("Accept-Encoding") ss := r.Header.Get("Accept-Encoding")
ss = strings.ToLower(ss) ss = strings.ToLower(ss)
if strings.Contains(ss, "gzip") { if strings.Contains(ss, "gzip") {
@ -203,22 +197,3 @@ func GetAcceptEncodingZip(r *http.Request) string {
} }
return "" return ""
} }
// CloseZWriter closes the io.Writer after compressing static file.
func CloseZWriter(zwriter io.Writer) {
if zwriter == nil {
return
}
switch zwriter.(type) {
case *gzip.Writer:
zwriter.(*gzip.Writer).Close()
case *flate.Writer:
zwriter.(*flate.Writer).Close()
//其他情况不close, 保持和默认(非压缩)行为一致
/*
case io.WriteCloser:
zwriter.(io.WriteCloser).Close()
*/
}
}

112
router.go
View File

@ -7,8 +7,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path"
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
@ -33,7 +31,7 @@ const (
var ( var (
// supported http methods. // supported http methods.
HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head"} HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head", "trace", "connect"}
// these beego.Controller's methods shouldn't reflect to AutoRouter // these beego.Controller's methods shouldn't reflect to AutoRouter
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp", "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp",
@ -544,88 +542,26 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
http.Error(w, "Method Not Allowed", 405) http.Error(w, "Method Not Allowed", 405)
goto Admin goto Admin
} }
//static file server
if serverStaticRouter(context) {
goto Admin
}
if context.Input.IsPost() {
if CopyRequestBody && !context.Input.IsUpload() {
context.Input.CopyBody()
}
context.Input.ParseFormOrMulitForm(MaxMemory)
}
if do_filter(BeforeRouter) { if do_filter(BeforeRouter) {
goto Admin goto Admin
} }
//static file server
for prefix, staticDir := range StaticDir {
if len(prefix) == 0 {
continue
}
if r.URL.Path == "/favicon.ico" {
file := path.Join(staticDir, r.URL.Path)
if utils.FileExists(file) {
http.ServeFile(w, r, file)
w.started = true
goto Admin
}
}
if strings.HasPrefix(r.URL.Path, prefix) {
if len(r.URL.Path) > len(prefix) && r.URL.Path[len(prefix)] != '/' {
continue
}
if r.URL.Path == prefix && prefix[len(prefix)-1] != '/' {
http.Redirect(rw, r, r.URL.Path+"/", 302)
goto Admin
}
file := path.Join(staticDir, r.URL.Path[len(prefix):])
finfo, err := os.Stat(file)
if err != nil {
if RunMode == "dev" {
Warn(err)
}
http.NotFound(w, r)
goto Admin
}
//if the request is dir and DirectoryIndex is false then
if finfo.IsDir() && !DirectoryIndex {
middleware.Exception("403", rw, r, "403 Forbidden")
goto Admin
}
//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 {
if EnableGzip {
w.contentEncoding = GetAcceptEncodingZip(r)
}
memzipfile, err := OpenMemZipFile(file, w.contentEncoding)
if err != nil {
return
}
w.InitHeadContent(finfo.Size())
http.ServeContent(w, r, file, finfo.ModTime(), memzipfile)
} else {
http.ServeFile(w, r, file)
}
w.started = true
goto Admin
}
}
if do_filter(AfterStatic) { if do_filter(AfterStatic) {
goto Admin goto Admin
} }
if CopyRequestBody {
context.Input.Body()
}
if context.Input.RunController != nil && context.Input.RunMethod != "" { if context.Input.RunController != nil && context.Input.RunMethod != "" {
findrouter = true findrouter = true
runMethod = context.Input.RunMethod runMethod = context.Input.RunMethod
@ -757,9 +693,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
if findrouter { if findrouter {
if r.Method == "POST" {
r.ParseMultipartForm(MaxMemory)
}
//execute middleware filters //execute middleware filters
if do_filter(BeforeExec) { if do_filter(BeforeExec) {
goto Admin goto Admin
@ -830,9 +763,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
} }
Admin:
do_filter(FinishRouter) do_filter(FinishRouter)
Admin:
//admin module record QPS //admin module record QPS
if EnableAdmin { if EnableAdmin {
timeend := time.Since(starttime) timeend := time.Since(starttime)
@ -891,10 +823,9 @@ func (p *ControllerRegistor) getRunMethod(method string, context *beecontext.Con
//responseWriter is a wrapper for the http.ResponseWriter //responseWriter is a wrapper for the http.ResponseWriter
//started set to true if response was written to then don't execute other handler //started set to true if response was written to then don't execute other handler
type responseWriter struct { type responseWriter struct {
writer http.ResponseWriter writer http.ResponseWriter
started bool started bool
status int status int
contentEncoding string
} }
// Header returns the header map that will be sent by WriteHeader. // Header returns the header map that will be sent by WriteHeader.
@ -902,17 +833,6 @@ func (w *responseWriter) Header() http.Header {
return w.writer.Header() return w.writer.Header()
} }
// Init content-length header.
func (w *responseWriter) InitHeadContent(contentlength int64) {
if w.contentEncoding == "gzip" {
w.Header().Set("Content-Encoding", "gzip")
} else if w.contentEncoding == "deflate" {
w.Header().Set("Content-Encoding", "deflate")
} else {
w.Header().Set("Content-Length", strconv.FormatInt(contentlength, 10))
}
}
// Write writes the data to the connection as part of an HTTP reply, // Write writes the data to the connection as part of an HTTP reply,
// and sets `started` to true. // and sets `started` to true.
// started means the response has sent out. // started means the response has sent out.

90
staticfile.go Normal file
View File

@ -0,0 +1,90 @@
package beego
import (
"net/http"
"os"
"path"
"strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils"
)
func serverStaticRouter(ctx *context.Context) bool {
requestPath := ctx.Input.Request.URL.Path
for prefix, staticDir := range StaticDir {
if len(prefix) == 0 {
continue
}
if requestPath == "/favicon.ico" {
file := path.Join(staticDir, requestPath)
if utils.FileExists(file) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return true
}
}
if strings.HasPrefix(requestPath, prefix) {
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
continue
}
if requestPath == prefix && prefix[len(prefix)-1] != '/' {
http.Redirect(ctx.ResponseWriter, ctx.Request, requestPath+"/", 302)
return true
}
file := path.Join(staticDir, requestPath[len(prefix):])
finfo, err := os.Stat(file)
if err != nil {
if RunMode == "dev" {
Warn(err)
}
http.NotFound(ctx.ResponseWriter, ctx.Request)
return true
}
//if the request is dir and DirectoryIndex is false then
if finfo.IsDir() && !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
return true
}
//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 err != nil {
return true
}
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)
}
return true
}
}
return false
}