3255a435 by astaxie

beego: move staticServer to New file

1 parent 73d757e3
...@@ -14,12 +14,12 @@ import ( ...@@ -14,12 +14,12 @@ import (
14 "time" 14 "time"
15 ) 15 )
16 16
17 var gmfim map[string]*MemFileInfo = make(map[string]*MemFileInfo) 17 var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo)
18 var lock sync.RWMutex 18 var lock sync.RWMutex
19 19
20 // OpenMemZipFile returns MemFile object with a compressed static file. 20 // OpenMemZipFile returns MemFile object with a compressed static file.
21 // it's used for serve static file if gzip enable. 21 // it's used for serve static file if gzip enable.
22 func OpenMemZipFile(path string, zip string) (*MemFile, error) { 22 func openMemZipFile(path string, zip string) (*memFile, error) {
23 osfile, e := os.Open(path) 23 osfile, e := os.Open(path)
24 if e != nil { 24 if e != nil {
25 return nil, e 25 return nil, e
...@@ -36,12 +36,9 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { ...@@ -36,12 +36,9 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
36 lock.RLock() 36 lock.RLock()
37 cfi, ok := gmfim[zip+":"+path] 37 cfi, ok := gmfim[zip+":"+path]
38 lock.RUnlock() 38 lock.RUnlock()
39 if ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize { 39 if !(ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize) {
40
41 } else {
42 var content []byte 40 var content []byte
43 if zip == "gzip" { 41 if zip == "gzip" {
44 //将文件内容压缩到zipbuf中
45 var zipbuf bytes.Buffer 42 var zipbuf bytes.Buffer
46 gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression) 43 gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression)
47 if e != nil { 44 if e != nil {
...@@ -52,13 +49,11 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { ...@@ -52,13 +49,11 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
52 if e != nil { 49 if e != nil {
53 return nil, e 50 return nil, e
54 } 51 }
55 //读zipbuf到content
56 content, e = ioutil.ReadAll(&zipbuf) 52 content, e = ioutil.ReadAll(&zipbuf)
57 if e != nil { 53 if e != nil {
58 return nil, e 54 return nil, e
59 } 55 }
60 } else if zip == "deflate" { 56 } else if zip == "deflate" {
61 //将文件内容压缩到zipbuf中
62 var zipbuf bytes.Buffer 57 var zipbuf bytes.Buffer
63 deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression) 58 deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression)
64 if e != nil { 59 if e != nil {
...@@ -69,7 +64,6 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { ...@@ -69,7 +64,6 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
69 if e != nil { 64 if e != nil {
70 return nil, e 65 return nil, e
71 } 66 }
72 //将zipbuf读入到content
73 content, e = ioutil.ReadAll(&zipbuf) 67 content, e = ioutil.ReadAll(&zipbuf)
74 if e != nil { 68 if e != nil {
75 return nil, e 69 return nil, e
...@@ -81,17 +75,17 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) { ...@@ -81,17 +75,17 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
81 } 75 }
82 } 76 }
83 77
84 cfi = &MemFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize} 78 cfi = &memFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize}
85 lock.Lock() 79 lock.Lock()
86 defer lock.Unlock() 80 defer lock.Unlock()
87 gmfim[zip+":"+path] = cfi 81 gmfim[zip+":"+path] = cfi
88 } 82 }
89 return &MemFile{fi: cfi, offset: 0}, nil 83 return &memFile{fi: cfi, offset: 0}, nil
90 } 84 }
91 85
92 // MemFileInfo contains a compressed file bytes and file information. 86 // MemFileInfo contains a compressed file bytes and file information.
93 // it implements os.FileInfo interface. 87 // it implements os.FileInfo interface.
94 type MemFileInfo struct { 88 type memFileInfo struct {
95 os.FileInfo 89 os.FileInfo
96 modTime time.Time 90 modTime time.Time
97 content []byte 91 content []byte
...@@ -100,62 +94,62 @@ type MemFileInfo struct { ...@@ -100,62 +94,62 @@ type MemFileInfo struct {
100 } 94 }
101 95
102 // Name returns the compressed filename. 96 // Name returns the compressed filename.
103 func (fi *MemFileInfo) Name() string { 97 func (fi *memFileInfo) Name() string {
104 return fi.Name() 98 return fi.Name()
105 } 99 }
106 100
107 // Size returns the raw file content size, not compressed size. 101 // Size returns the raw file content size, not compressed size.
108 func (fi *MemFileInfo) Size() int64 { 102 func (fi *memFileInfo) Size() int64 {
109 return fi.contentSize 103 return fi.contentSize
110 } 104 }
111 105
112 // Mode returns file mode. 106 // Mode returns file mode.
113 func (fi *MemFileInfo) Mode() os.FileMode { 107 func (fi *memFileInfo) Mode() os.FileMode {
114 return fi.Mode() 108 return fi.Mode()
115 } 109 }
116 110
117 // ModTime returns the last modified time of raw file. 111 // ModTime returns the last modified time of raw file.
118 func (fi *MemFileInfo) ModTime() time.Time { 112 func (fi *memFileInfo) ModTime() time.Time {
119 return fi.modTime 113 return fi.modTime
120 } 114 }
121 115
122 // IsDir returns the compressing file is a directory or not. 116 // IsDir returns the compressing file is a directory or not.
123 func (fi *MemFileInfo) IsDir() bool { 117 func (fi *memFileInfo) IsDir() bool {
124 return fi.IsDir() 118 return fi.IsDir()
125 } 119 }
126 120
127 // return nil. implement the os.FileInfo interface method. 121 // return nil. implement the os.FileInfo interface method.
128 func (fi *MemFileInfo) Sys() interface{} { 122 func (fi *memFileInfo) Sys() interface{} {
129 return nil 123 return nil
130 } 124 }
131 125
132 // MemFile contains MemFileInfo and bytes offset when reading. 126 // MemFile contains MemFileInfo and bytes offset when reading.
133 // it implements io.Reader,io.ReadCloser and io.Seeker. 127 // it implements io.Reader,io.ReadCloser and io.Seeker.
134 type MemFile struct { 128 type memFile struct {
135 fi *MemFileInfo 129 fi *memFileInfo
136 offset int64 130 offset int64
137 } 131 }
138 132
139 // Close memfile. 133 // Close memfile.
140 func (f *MemFile) Close() error { 134 func (f *memFile) Close() error {
141 return nil 135 return nil
142 } 136 }
143 137
144 // Get os.FileInfo of memfile. 138 // Get os.FileInfo of memfile.
145 func (f *MemFile) Stat() (os.FileInfo, error) { 139 func (f *memFile) Stat() (os.FileInfo, error) {
146 return f.fi, nil 140 return f.fi, nil
147 } 141 }
148 142
149 // read os.FileInfo of files in directory of memfile. 143 // read os.FileInfo of files in directory of memfile.
150 // it returns empty slice. 144 // it returns empty slice.
151 func (f *MemFile) Readdir(count int) ([]os.FileInfo, error) { 145 func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
152 infos := []os.FileInfo{} 146 infos := []os.FileInfo{}
153 147
154 return infos, nil 148 return infos, nil
155 } 149 }
156 150
157 // Read bytes from the compressed file bytes. 151 // Read bytes from the compressed file bytes.
158 func (f *MemFile) Read(p []byte) (n int, err error) { 152 func (f *memFile) Read(p []byte) (n int, err error) {
159 if len(f.fi.content)-int(f.offset) >= len(p) { 153 if len(f.fi.content)-int(f.offset) >= len(p) {
160 n = len(p) 154 n = len(p)
161 } else { 155 } else {
...@@ -171,7 +165,7 @@ var errWhence = errors.New("Seek: invalid whence") ...@@ -171,7 +165,7 @@ var errWhence = errors.New("Seek: invalid whence")
171 var errOffset = errors.New("Seek: invalid offset") 165 var errOffset = errors.New("Seek: invalid offset")
172 166
173 // Read bytes from the compressed file bytes by seeker. 167 // Read bytes from the compressed file bytes by seeker.
174 func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) { 168 func (f *memFile) Seek(offset int64, whence int) (ret int64, err error) {
175 switch whence { 169 switch whence {
176 default: 170 default:
177 return 0, errWhence 171 return 0, errWhence
...@@ -191,7 +185,7 @@ func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) { ...@@ -191,7 +185,7 @@ func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) {
191 // GetAcceptEncodingZip returns accept encoding format in http header. 185 // GetAcceptEncodingZip returns accept encoding format in http header.
192 // zip is first, then deflate if both accepted. 186 // zip is first, then deflate if both accepted.
193 // If no accepted, return empty string. 187 // If no accepted, return empty string.
194 func GetAcceptEncodingZip(r *http.Request) string { 188 func getAcceptEncodingZip(r *http.Request) string {
195 ss := r.Header.Get("Accept-Encoding") 189 ss := r.Header.Get("Accept-Encoding")
196 ss = strings.ToLower(ss) 190 ss = strings.ToLower(ss)
197 if strings.Contains(ss, "gzip") { 191 if strings.Contains(ss, "gzip") {
...@@ -203,22 +197,3 @@ func GetAcceptEncodingZip(r *http.Request) string { ...@@ -203,22 +197,3 @@ func GetAcceptEncodingZip(r *http.Request) string {
203 } 197 }
204 return "" 198 return ""
205 } 199 }
206
207 // CloseZWriter closes the io.Writer after compressing static file.
208 func CloseZWriter(zwriter io.Writer) {
209 if zwriter == nil {
210 return
211 }
212
213 switch zwriter.(type) {
214 case *gzip.Writer:
215 zwriter.(*gzip.Writer).Close()
216 case *flate.Writer:
217 zwriter.(*flate.Writer).Close()
218 //其他情况不close, 保持和默认(非压缩)行为一致
219 /*
220 case io.WriteCloser:
221 zwriter.(io.WriteCloser).Close()
222 */
223 }
224 }
......
...@@ -7,8 +7,6 @@ import ( ...@@ -7,8 +7,6 @@ import (
7 "net" 7 "net"
8 "net/http" 8 "net/http"
9 "net/url" 9 "net/url"
10 "os"
11 "path"
12 "reflect" 10 "reflect"
13 "regexp" 11 "regexp"
14 "runtime" 12 "runtime"
...@@ -33,7 +31,7 @@ const ( ...@@ -33,7 +31,7 @@ const (
33 31
34 var ( 32 var (
35 // supported http methods. 33 // supported http methods.
36 HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head"} 34 HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head", "trace", "connect"}
37 // these beego.Controller's methods shouldn't reflect to AutoRouter 35 // these beego.Controller's methods shouldn't reflect to AutoRouter
38 exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", 36 exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
39 "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp", 37 "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp",
...@@ -544,86 +542,24 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -544,86 +542,24 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
544 http.Error(w, "Method Not Allowed", 405) 542 http.Error(w, "Method Not Allowed", 405)
545 goto Admin 543 goto Admin
546 } 544 }
547 545 //static file server
548 if do_filter(BeforeRouter) { 546 if serverStaticRouter(context) {
549 goto Admin 547 goto Admin
550 } 548 }
551 549
552 //static file server 550 if context.Input.IsPost() {
553 for prefix, staticDir := range StaticDir { 551 if CopyRequestBody && !context.Input.IsUpload() {
554 if len(prefix) == 0 { 552 context.Input.CopyBody()
555 continue
556 }
557 if r.URL.Path == "/favicon.ico" {
558 file := path.Join(staticDir, r.URL.Path)
559 if utils.FileExists(file) {
560 http.ServeFile(w, r, file)
561 w.started = true
562 goto Admin
563 }
564 }
565 if strings.HasPrefix(r.URL.Path, prefix) {
566 if len(r.URL.Path) > len(prefix) && r.URL.Path[len(prefix)] != '/' {
567 continue
568 }
569 if r.URL.Path == prefix && prefix[len(prefix)-1] != '/' {
570 http.Redirect(rw, r, r.URL.Path+"/", 302)
571 goto Admin
572 }
573 file := path.Join(staticDir, r.URL.Path[len(prefix):])
574 finfo, err := os.Stat(file)
575 if err != nil {
576 if RunMode == "dev" {
577 Warn(err)
578 }
579 http.NotFound(w, r)
580 goto Admin
581 }
582 //if the request is dir and DirectoryIndex is false then
583 if finfo.IsDir() && !DirectoryIndex {
584 middleware.Exception("403", rw, r, "403 Forbidden")
585 goto Admin
586 }
587
588 //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request
589 isStaticFileToCompress := false
590 if StaticExtensionsToGzip != nil && len(StaticExtensionsToGzip) > 0 {
591 for _, statExtension := range StaticExtensionsToGzip {
592 if strings.HasSuffix(strings.ToLower(file), strings.ToLower(statExtension)) {
593 isStaticFileToCompress = true
594 break
595 }
596 }
597 }
598
599 if isStaticFileToCompress {
600 if EnableGzip {
601 w.contentEncoding = GetAcceptEncodingZip(r)
602 }
603
604 memzipfile, err := OpenMemZipFile(file, w.contentEncoding)
605 if err != nil {
606 return
607 }
608
609 w.InitHeadContent(finfo.Size())
610
611 http.ServeContent(w, r, file, finfo.ModTime(), memzipfile)
612 } else {
613 http.ServeFile(w, r, file)
614 }
615
616 w.started = true
617 goto Admin
618 } 553 }
554 context.Input.ParseFormOrMulitForm(MaxMemory)
619 } 555 }
620 556
621 if do_filter(AfterStatic) { 557 if do_filter(BeforeRouter) {
622 goto Admin 558 goto Admin
623 } 559 }
624 560
625 if CopyRequestBody { 561 if do_filter(AfterStatic) {
626 context.Input.Body() 562 goto Admin
627 } 563 }
628 564
629 if context.Input.RunController != nil && context.Input.RunMethod != "" { 565 if context.Input.RunController != nil && context.Input.RunMethod != "" {
...@@ -757,9 +693,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -757,9 +693,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
757 } 693 }
758 694
759 if findrouter { 695 if findrouter {
760 if r.Method == "POST" {
761 r.ParseMultipartForm(MaxMemory)
762 }
763 //execute middleware filters 696 //execute middleware filters
764 if do_filter(BeforeExec) { 697 if do_filter(BeforeExec) {
765 goto Admin 698 goto Admin
...@@ -830,9 +763,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -830,9 +763,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
830 } 763 }
831 } 764 }
832 765
833 Admin:
834 do_filter(FinishRouter) 766 do_filter(FinishRouter)
835 767 Admin:
836 //admin module record QPS 768 //admin module record QPS
837 if EnableAdmin { 769 if EnableAdmin {
838 timeend := time.Since(starttime) 770 timeend := time.Since(starttime)
...@@ -891,10 +823,9 @@ func (p *ControllerRegistor) getRunMethod(method string, context *beecontext.Con ...@@ -891,10 +823,9 @@ func (p *ControllerRegistor) getRunMethod(method string, context *beecontext.Con
891 //responseWriter is a wrapper for the http.ResponseWriter 823 //responseWriter is a wrapper for the http.ResponseWriter
892 //started set to true if response was written to then don't execute other handler 824 //started set to true if response was written to then don't execute other handler
893 type responseWriter struct { 825 type responseWriter struct {
894 writer http.ResponseWriter 826 writer http.ResponseWriter
895 started bool 827 started bool
896 status int 828 status int
897 contentEncoding string
898 } 829 }
899 830
900 // Header returns the header map that will be sent by WriteHeader. 831 // Header returns the header map that will be sent by WriteHeader.
...@@ -902,17 +833,6 @@ func (w *responseWriter) Header() http.Header { ...@@ -902,17 +833,6 @@ func (w *responseWriter) Header() http.Header {
902 return w.writer.Header() 833 return w.writer.Header()
903 } 834 }
904 835
905 // Init content-length header.
906 func (w *responseWriter) InitHeadContent(contentlength int64) {
907 if w.contentEncoding == "gzip" {
908 w.Header().Set("Content-Encoding", "gzip")
909 } else if w.contentEncoding == "deflate" {
910 w.Header().Set("Content-Encoding", "deflate")
911 } else {
912 w.Header().Set("Content-Length", strconv.FormatInt(contentlength, 10))
913 }
914 }
915
916 // Write writes the data to the connection as part of an HTTP reply, 836 // Write writes the data to the connection as part of an HTTP reply,
917 // and sets `started` to true. 837 // and sets `started` to true.
918 // started means the response has sent out. 838 // started means the response has sent out.
......
1 package beego
2
3 import (
4 "net/http"
5 "os"
6 "path"
7 "strconv"
8 "strings"
9
10 "github.com/astaxie/beego/context"
11 "github.com/astaxie/beego/middleware"
12 "github.com/astaxie/beego/utils"
13 )
14
15 func serverStaticRouter(ctx *context.Context) bool {
16 requestPath := ctx.Input.Request.URL.Path
17 for prefix, staticDir := range StaticDir {
18 if len(prefix) == 0 {
19 continue
20 }
21 if requestPath == "/favicon.ico" {
22 file := path.Join(staticDir, requestPath)
23 if utils.FileExists(file) {
24 http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
25 return true
26 }
27 }
28 if strings.HasPrefix(requestPath, prefix) {
29 if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
30 continue
31 }
32 if requestPath == prefix && prefix[len(prefix)-1] != '/' {
33 http.Redirect(ctx.ResponseWriter, ctx.Request, requestPath+"/", 302)
34 return true
35 }
36 file := path.Join(staticDir, requestPath[len(prefix):])
37 finfo, err := os.Stat(file)
38 if err != nil {
39 if RunMode == "dev" {
40 Warn(err)
41 }
42 http.NotFound(ctx.ResponseWriter, ctx.Request)
43 return true
44 }
45 //if the request is dir and DirectoryIndex is false then
46 if finfo.IsDir() && !DirectoryIndex {
47 middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
48 return true
49 }
50
51 //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request
52 isStaticFileToCompress := false
53 if StaticExtensionsToGzip != nil && len(StaticExtensionsToGzip) > 0 {
54 for _, statExtension := range StaticExtensionsToGzip {
55 if strings.HasSuffix(strings.ToLower(file), strings.ToLower(statExtension)) {
56 isStaticFileToCompress = true
57 break
58 }
59 }
60 }
61
62 if isStaticFileToCompress {
63 var contentEncoding string
64 if EnableGzip {
65 contentEncoding = getAcceptEncodingZip(ctx.Request)
66 }
67
68 memzipfile, err := openMemZipFile(file, contentEncoding)
69 if err != nil {
70 return true
71 }
72
73 if contentEncoding == "gzip" {
74 ctx.Output.Header("Content-Encoding", "gzip")
75 } else if contentEncoding == "deflate" {
76 ctx.Output.Header("Content-Encoding", "deflate")
77 } else {
78 ctx.Output.Header("Content-Length", strconv.FormatInt(finfo.Size(), 10))
79 }
80
81 http.ServeContent(ctx.ResponseWriter, ctx.Request, file, finfo.ModTime(), memzipfile)
82
83 } else {
84 http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
85 }
86 return true
87 }
88 }
89 return false
90 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!