github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_handler.go (about) 1 // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 package ghttp 8 9 import ( 10 "fmt" 11 "github.com/zhongdalu/gf/g/encoding/ghtml" 12 "github.com/zhongdalu/gf/g/os/gspath" 13 "github.com/zhongdalu/gf/g/os/gtime" 14 "net/http" 15 "os" 16 "reflect" 17 "sort" 18 "strings" 19 ) 20 21 // 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行 22 func (s *Server) defaultHttpHandle(w http.ResponseWriter, r *http.Request) { 23 s.handleRequest(w, r) 24 } 25 26 // 执行处理HTTP请求, 27 // 首先,查找是否有对应域名的处理接口配置; 28 // 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置; 29 // 最后,如果以上都没有找到处理接口,那么进行文件处理; 30 func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { 31 // 重写规则判断 32 if len(s.config.Rewrites) > 0 { 33 if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok { 34 r.URL.Path = rewrite 35 } 36 } 37 38 // URI默认值 39 if r.URL.Path == "" { 40 r.URL.Path = "/" 41 } 42 43 // 去掉末尾的"/"号 44 if r.URL.Path != "/" { 45 for r.URL.Path[len(r.URL.Path)-1] == '/' { 46 r.URL.Path = r.URL.Path[:len(r.URL.Path)-1] 47 } 48 } 49 50 // 创建请求处理对象 51 request := newRequest(s, r, w) 52 53 defer func() { 54 // 设置请求完成时间 55 request.LeaveTime = gtime.Microsecond() 56 // 事件 - BeforeOutput 57 if !request.IsExited() { 58 s.callHookHandler(HOOK_BEFORE_OUTPUT, request) 59 } 60 // 如果没有产生异常状态,那么设置返回状态为200 61 if request.Response.Status == 0 { 62 request.Response.Status = http.StatusOK 63 } 64 // error log 65 if e := recover(); e != nil { 66 request.Response.WriteStatus(http.StatusInternalServerError) 67 s.handleErrorLog(e, request) 68 } 69 // access log 70 s.handleAccessLog(request) 71 // 输出Cookie 72 request.Cookie.Output() 73 // 输出缓冲区 74 request.Response.Output() 75 // 事件 - AfterOutput 76 if !request.IsExited() { 77 s.callHookHandler(HOOK_AFTER_OUTPUT, request) 78 } 79 // 更新Session会话超时时间 80 request.Session.UpdateExpire() 81 }() 82 83 // ============================================================ 84 // 优先级控制: 85 // 静态文件 > 动态服务 > 静态目录 86 // ============================================================ 87 88 staticFile := "" 89 isStaticDir := false 90 // 优先执行静态文件检索(检测是否存在对应的静态文件,包括index files处理) 91 if s.config.FileServerEnabled { 92 staticFile, isStaticDir = s.searchStaticFile(r.URL.Path) 93 if staticFile != "" { 94 request.isFileRequest = true 95 } 96 } 97 98 // 动态服务检索 99 handler := (*handlerItem)(nil) 100 if !request.isFileRequest || isStaticDir { 101 if parsedItem := s.getServeHandlerWithCache(request); parsedItem != nil { 102 handler = parsedItem.handler 103 for k, v := range parsedItem.values { 104 request.routerVars[k] = v 105 } 106 request.Router = parsedItem.handler.router 107 } 108 } 109 110 // 判断最终对该请求提供的服务方式 111 if isStaticDir && handler != nil { 112 request.isFileRequest = false 113 } 114 115 // 事件 - BeforeServe 116 s.callHookHandler(HOOK_BEFORE_SERVE, request) 117 118 // 执行静态文件服务/回调控制器/执行对象/方法 119 if !request.IsExited() { 120 // 需要再次判断文件是否真实存在, 121 // 因为文件检索可能使用了缓存,从健壮性考虑这里需要二次判断 122 if request.isFileRequest /* && gfile.Exists(staticFile) */ { 123 s.serveFile(request, staticFile) 124 } else { 125 if handler != nil { 126 // 动态服务 127 s.callServeHandler(handler, request) 128 } else { 129 if isStaticDir { 130 // 静态目录 131 s.serveFile(request, staticFile) 132 } else { 133 if len(request.Response.Header()) == 0 && 134 request.Response.Status == 0 && 135 request.Response.BufferLength() == 0 { 136 request.Response.WriteStatus(http.StatusNotFound) 137 } 138 } 139 } 140 } 141 } 142 143 // 事件 - AfterServe 144 if !request.IsExited() { 145 s.callHookHandler(HOOK_AFTER_SERVE, request) 146 } 147 } 148 149 // 查找静态文件的绝对路径 150 func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) { 151 // 优先查找URI映射 152 if len(s.config.StaticPaths) > 0 { 153 for _, item := range s.config.StaticPaths { 154 if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) { 155 // 防止类似 /static/style 映射到 /static/style.css 的情况 156 if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' { 157 continue 158 } 159 return gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...) 160 } 161 } 162 } 163 // 其次查找root和search path 164 if len(s.config.SearchPaths) > 0 { 165 for _, path := range s.config.SearchPaths { 166 if filePath, isDir = gspath.Search(path, uri, s.config.IndexFiles...); filePath != "" { 167 return filePath, isDir 168 } 169 } 170 } 171 return "", false 172 } 173 174 // 调用服务接口 175 func (s *Server) callServeHandler(h *handlerItem, r *Request) { 176 if h.faddr == nil { 177 c := reflect.New(h.ctype) 178 s.niceCallFunc(func() { 179 c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r)}) 180 }) 181 if !r.IsExited() { 182 s.niceCallFunc(func() { 183 c.MethodByName(h.fname).Call(nil) 184 }) 185 } 186 if !r.IsExited() { 187 s.niceCallFunc(func() { 188 c.MethodByName("Shut").Call(nil) 189 }) 190 } 191 } else { 192 if h.finit != nil { 193 s.niceCallFunc(func() { 194 h.finit(r) 195 }) 196 } 197 if !r.IsExited() { 198 s.niceCallFunc(func() { 199 h.faddr(r) 200 }) 201 } 202 if h.fshut != nil && !r.IsExited() { 203 s.niceCallFunc(func() { 204 h.fshut(r) 205 }) 206 } 207 } 208 } 209 210 // 友好地调用方法 211 func (s *Server) niceCallFunc(f func()) { 212 defer func() { 213 if err := recover(); err != nil { 214 switch err { 215 case gEXCEPTION_EXIT: 216 fallthrough 217 case gEXCEPTION_EXIT_ALL: 218 return 219 default: 220 panic(err) 221 } 222 } 223 }() 224 f() 225 } 226 227 // http server静态文件处理,path可以为相对路径也可以为绝对路径 228 func (s *Server) serveFile(r *Request, path string) { 229 f, err := os.Open(path) 230 if err != nil { 231 r.Response.WriteStatus(http.StatusForbidden) 232 return 233 } 234 defer f.Close() 235 info, _ := f.Stat() 236 if info.IsDir() { 237 if s.config.IndexFolder { 238 s.listDir(r, f) 239 } else { 240 r.Response.WriteStatus(http.StatusForbidden) 241 } 242 } else { 243 // 读取文件内容返回, no buffer 244 http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), f) 245 } 246 } 247 248 // 显示目录列表 249 func (s *Server) listDir(r *Request, f http.File) { 250 files, err := f.Readdir(-1) 251 if err != nil { 252 r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory") 253 return 254 } 255 sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() }) 256 257 r.Response.Header().Set("Content-Type", "text/html; charset=utf-8") 258 r.Response.Write("<pre>\n") 259 if r.URL.Path != "/" { 260 r.Response.Write(fmt.Sprint("<a href=\"..\">..</a>\n")) 261 } 262 for _, file := range files { 263 name := file.Name() 264 if file.IsDir() { 265 name += "/" 266 } 267 r.Response.Write(fmt.Sprintf("<a href=\"%s\">%s</a>\n", name, ghtml.SpecialChars(name))) 268 } 269 r.Response.Write("</pre>\n") 270 }