From 3255a4356831521b89b0300f67d68302c76f23ab Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 6 Apr 2014 00:18:21 +0800 Subject: [PATCH] beego: move staticServer to New file --- memzipfile.go | 65 +++++++++-------------------- router.go | 112 ++++++++------------------------------------------ staticfile.go | 90 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 141 deletions(-) create mode 100644 staticfile.go diff --git a/memzipfile.go b/memzipfile.go index 8d3edd9c..c62e2bc4 100644 --- a/memzipfile.go +++ b/memzipfile.go @@ -14,12 +14,12 @@ import ( "time" ) -var gmfim map[string]*MemFileInfo = make(map[string]*MemFileInfo) +var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo) var lock sync.RWMutex // 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) { +func openMemZipFile(path string, zip string) (*memFile, error) { osfile, e := os.Open(path) if e != nil { return nil, e @@ -36,12 +36,9 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { lock.RLock() cfi, ok := gmfim[zip+":"+path] lock.RUnlock() - if ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize { - - } else { + if !(ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize) { var content []byte if zip == "gzip" { - //将文件内容压缩到zipbuf中 var zipbuf bytes.Buffer gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression) if e != nil { @@ -52,13 +49,11 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { if e != nil { return nil, e } - //读zipbuf到content content, e = ioutil.ReadAll(&zipbuf) if e != nil { return nil, e } } else if zip == "deflate" { - //将文件内容压缩到zipbuf中 var zipbuf bytes.Buffer deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression) if e != nil { @@ -69,7 +64,6 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { if e != nil { return nil, e } - //将zipbuf读入到content content, e = ioutil.ReadAll(&zipbuf) if e != nil { 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() defer lock.Unlock() 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. // it implements os.FileInfo interface. -type MemFileInfo struct { +type memFileInfo struct { os.FileInfo modTime time.Time content []byte @@ -100,62 +94,62 @@ type MemFileInfo struct { } // Name returns the compressed filename. -func (fi *MemFileInfo) Name() string { +func (fi *memFileInfo) Name() string { return fi.Name() } // Size returns the raw file content size, not compressed size. -func (fi *MemFileInfo) Size() int64 { +func (fi *memFileInfo) Size() int64 { return fi.contentSize } // Mode returns file mode. -func (fi *MemFileInfo) Mode() os.FileMode { +func (fi *memFileInfo) Mode() os.FileMode { return fi.Mode() } // ModTime returns the last modified time of raw file. -func (fi *MemFileInfo) ModTime() time.Time { +func (fi *memFileInfo) ModTime() time.Time { return fi.modTime } // IsDir returns the compressing file is a directory or not. -func (fi *MemFileInfo) IsDir() bool { +func (fi *memFileInfo) IsDir() bool { return fi.IsDir() } // return nil. implement the os.FileInfo interface method. -func (fi *MemFileInfo) Sys() interface{} { +func (fi *memFileInfo) Sys() interface{} { return nil } // MemFile contains MemFileInfo and bytes offset when reading. // it implements io.Reader,io.ReadCloser and io.Seeker. -type MemFile struct { - fi *MemFileInfo +type memFile struct { + fi *memFileInfo offset int64 } // Close memfile. -func (f *MemFile) Close() error { +func (f *memFile) Close() error { return nil } // Get os.FileInfo of memfile. -func (f *MemFile) Stat() (os.FileInfo, error) { +func (f *memFile) Stat() (os.FileInfo, error) { return f.fi, nil } // read os.FileInfo of files in directory of memfile. // 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{} return infos, nil } // 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) { n = len(p) } else { @@ -171,7 +165,7 @@ var errWhence = errors.New("Seek: invalid whence") var errOffset = errors.New("Seek: invalid offset") // 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 { default: 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. // zip is first, then deflate if both accepted. // 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 = strings.ToLower(ss) if strings.Contains(ss, "gzip") { @@ -203,22 +197,3 @@ func GetAcceptEncodingZip(r *http.Request) string { } 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() - */ - } -} diff --git a/router.go b/router.go index e21750f4..e123ec55 100644 --- a/router.go +++ b/router.go @@ -7,8 +7,6 @@ import ( "net" "net/http" "net/url" - "os" - "path" "reflect" "regexp" "runtime" @@ -33,7 +31,7 @@ const ( var ( // 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 exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", "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) 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) { 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) { goto Admin } - if CopyRequestBody { - context.Input.Body() - } - if context.Input.RunController != nil && context.Input.RunMethod != "" { findrouter = true runMethod = context.Input.RunMethod @@ -757,9 +693,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if findrouter { - if r.Method == "POST" { - r.ParseMultipartForm(MaxMemory) - } //execute middleware filters if do_filter(BeforeExec) { goto Admin @@ -830,9 +763,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } } -Admin: do_filter(FinishRouter) - +Admin: //admin module record QPS if EnableAdmin { 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 //started set to true if response was written to then don't execute other handler type responseWriter struct { - writer http.ResponseWriter - started bool - status int - contentEncoding string + writer http.ResponseWriter + started bool + status int } // 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() } -// 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, // and sets `started` to true. // started means the response has sent out. diff --git a/staticfile.go b/staticfile.go new file mode 100644 index 00000000..c248135c --- /dev/null +++ b/staticfile.go @@ -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 +}