2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
//
|
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2017-12-31 18:19:58 +08:00
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
package ghttp
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/encoding/ghtml"
|
|
|
|
|
|
"github.com/gogf/gf/g/os/gspath"
|
|
|
|
|
|
"github.com/gogf/gf/g/os/gtime"
|
2018-11-06 18:53:25 +08:00
|
|
|
|
"net/http"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"reflect"
|
|
|
|
|
|
"sort"
|
2018-11-30 09:48:57 +08:00
|
|
|
|
"strings"
|
2017-11-23 10:21:28 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2017-12-08 12:03:21 +08:00
|
|
|
|
// 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行
|
2017-11-23 10:21:28 +08:00
|
|
|
|
func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
|
2017-12-08 12:03:21 +08:00
|
|
|
|
s.handleRequest(w, r)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-05 17:06:37 +08:00
|
|
|
|
// 执行处理HTTP请求,
|
2017-12-18 10:42:59 +08:00
|
|
|
|
// 首先,查找是否有对应域名的处理接口配置;
|
|
|
|
|
|
// 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置;
|
|
|
|
|
|
// 最后,如果以上都没有找到处理接口,那么进行文件处理;
|
2017-12-08 12:03:21 +08:00
|
|
|
|
func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
2018-12-10 20:24:20 +08:00
|
|
|
|
// 重写规则判断
|
|
|
|
|
|
if len(s.config.Rewrites) > 0 {
|
|
|
|
|
|
if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok {
|
|
|
|
|
|
r.URL.Path = rewrite
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-03-31 20:52:30 +08:00
|
|
|
|
|
|
|
|
|
|
// URI默认值
|
|
|
|
|
|
if r.URL.Path == "" {
|
|
|
|
|
|
r.URL.Path = "/"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-16 16:23:34 +08:00
|
|
|
|
// 去掉末尾的"/"号
|
|
|
|
|
|
if r.URL.Path != "/" {
|
2018-11-23 09:20:45 +08:00
|
|
|
|
for r.URL.Path[len(r.URL.Path) - 1] == '/' {
|
|
|
|
|
|
r.URL.Path = r.URL.Path[:len(r.URL.Path) - 1]
|
|
|
|
|
|
}
|
2018-04-16 16:23:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-14 01:05:46 +08:00
|
|
|
|
// 创建请求处理对象
|
2018-04-11 16:06:45 +08:00
|
|
|
|
request := newRequest(s, r, w)
|
2018-04-19 19:11:10 +08:00
|
|
|
|
|
|
|
|
|
|
defer func() {
|
2019-02-18 16:12:59 +08:00
|
|
|
|
// 设置请求完成时间
|
|
|
|
|
|
request.LeaveTime = gtime.Microsecond()
|
|
|
|
|
|
// 事件 - BeforeOutput
|
|
|
|
|
|
if !request.IsExited() {
|
|
|
|
|
|
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
|
2018-04-29 23:19:02 +08:00
|
|
|
|
}
|
2019-05-06 09:35:39 +08:00
|
|
|
|
// 如果没有产生异常状态,那么设置返回状态为200
|
|
|
|
|
|
if request.Response.Status == 0 {
|
|
|
|
|
|
request.Response.Status = http.StatusOK
|
|
|
|
|
|
}
|
2019-03-28 09:34:16 +08:00
|
|
|
|
// error log
|
|
|
|
|
|
if e := recover(); e != nil {
|
|
|
|
|
|
request.Response.WriteStatus(http.StatusInternalServerError)
|
|
|
|
|
|
s.handleErrorLog(e, request)
|
|
|
|
|
|
}
|
|
|
|
|
|
// access log
|
|
|
|
|
|
s.handleAccessLog(request)
|
2019-02-18 16:12:59 +08:00
|
|
|
|
// 输出Cookie
|
|
|
|
|
|
request.Cookie.Output()
|
|
|
|
|
|
// 输出缓冲区
|
2019-03-05 17:06:37 +08:00
|
|
|
|
request.Response.Output()
|
2019-02-18 16:12:59 +08:00
|
|
|
|
// 事件 - AfterOutput
|
|
|
|
|
|
if !request.IsExited() {
|
|
|
|
|
|
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
|
|
|
|
|
|
}
|
2018-11-16 18:20:09 +08:00
|
|
|
|
// 更新Session会话超时时间
|
|
|
|
|
|
request.Session.UpdateExpire()
|
2018-04-19 19:11:10 +08:00
|
|
|
|
}()
|
|
|
|
|
|
|
2018-11-17 02:39:23 +08:00
|
|
|
|
// ============================================================
|
|
|
|
|
|
// 优先级控制:
|
|
|
|
|
|
// 静态文件 > 动态服务 > 静态目录
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
2018-11-23 09:20:45 +08:00
|
|
|
|
staticFile := ""
|
|
|
|
|
|
isStaticDir := false
|
2018-11-16 18:20:09 +08:00
|
|
|
|
// 优先执行静态文件检索(检测是否存在对应的静态文件,包括index files处理)
|
2018-11-23 09:20:45 +08:00
|
|
|
|
if s.config.FileServerEnabled {
|
2018-11-30 09:48:57 +08:00
|
|
|
|
staticFile, isStaticDir = s.searchStaticFile(r.URL.Path)
|
2018-11-23 09:20:45 +08:00
|
|
|
|
if staticFile != "" {
|
|
|
|
|
|
request.isFileRequest = true
|
|
|
|
|
|
}
|
2018-08-24 14:57:49 +08:00
|
|
|
|
}
|
2018-11-17 02:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
// 动态服务检索
|
|
|
|
|
|
handler := (*handlerItem)(nil)
|
2019-03-05 17:06:37 +08:00
|
|
|
|
if !request.isFileRequest || isStaticDir {
|
2018-11-17 02:39:23 +08:00
|
|
|
|
if parsedItem := s.getServeHandlerWithCache(request); parsedItem != nil {
|
|
|
|
|
|
handler = parsedItem.handler
|
|
|
|
|
|
for k, v := range parsedItem.values {
|
|
|
|
|
|
request.routerVars[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
request.Router = parsedItem.handler.router
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-08-24 14:57:49 +08:00
|
|
|
|
|
2018-11-23 09:20:45 +08:00
|
|
|
|
// 判断最终对该请求提供的服务方式
|
|
|
|
|
|
if isStaticDir && handler != nil {
|
|
|
|
|
|
request.isFileRequest = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-16 18:17:47 +08:00
|
|
|
|
// 事件 - BeforeServe
|
|
|
|
|
|
s.callHookHandler(HOOK_BEFORE_SERVE, request)
|
|
|
|
|
|
|
2018-08-24 14:57:49 +08:00
|
|
|
|
// 执行静态文件服务/回调控制器/执行对象/方法
|
2018-11-16 18:20:09 +08:00
|
|
|
|
if !request.IsExited() {
|
2019-03-05 17:06:37 +08:00
|
|
|
|
// 需要再次判断文件是否真实存在,
|
|
|
|
|
|
// 因为文件检索可能使用了缓存,从健壮性考虑这里需要二次判断
|
2018-11-23 09:20:45 +08:00
|
|
|
|
if request.isFileRequest /* && gfile.Exists(staticFile) */{
|
2018-11-16 18:20:09 +08:00
|
|
|
|
s.serveFile(request, staticFile)
|
2018-05-31 12:07:31 +08:00
|
|
|
|
} else {
|
2018-08-24 14:57:49 +08:00
|
|
|
|
if handler != nil {
|
2018-11-17 02:39:23 +08:00
|
|
|
|
// 动态服务
|
2018-08-24 14:57:49 +08:00
|
|
|
|
s.callServeHandler(handler, request)
|
2018-08-23 00:31:14 +08:00
|
|
|
|
} else {
|
2018-11-17 02:39:23 +08:00
|
|
|
|
if isStaticDir {
|
|
|
|
|
|
// 静态目录
|
|
|
|
|
|
s.serveFile(request, staticFile)
|
|
|
|
|
|
} else {
|
2019-01-29 23:01:14 +08:00
|
|
|
|
if len(request.Response.Header()) == 0 &&
|
|
|
|
|
|
request.Response.Status == 0 &&
|
|
|
|
|
|
request.Response.BufferLength() == 0 {
|
|
|
|
|
|
request.Response.WriteStatus(http.StatusNotFound)
|
|
|
|
|
|
}
|
2018-11-17 02:39:23 +08:00
|
|
|
|
}
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-05-31 12:07:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-14 01:05:46 +08:00
|
|
|
|
// 事件 - AfterServe
|
2019-01-13 00:43:36 +08:00
|
|
|
|
if !request.IsExited() {
|
|
|
|
|
|
s.callHookHandler(HOOK_AFTER_SERVE, request)
|
|
|
|
|
|
}
|
2018-04-11 12:05:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-30 09:48:57 +08:00
|
|
|
|
// 查找静态文件的绝对路径
|
|
|
|
|
|
func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) {
|
|
|
|
|
|
// 优先查找URI映射
|
|
|
|
|
|
if len(s.config.StaticPaths) > 0 {
|
|
|
|
|
|
for _, item := range s.config.StaticPaths {
|
|
|
|
|
|
if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0 : len(item.prefix)]) {
|
|
|
|
|
|
// 防止类似 /static/style 映射到 /static/style.css 的情况
|
|
|
|
|
|
if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
return gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 其次查找root和search path
|
|
|
|
|
|
if len(s.config.SearchPaths) > 0 {
|
|
|
|
|
|
for _, path := range s.config.SearchPaths {
|
|
|
|
|
|
if filePath, isDir = gspath.Search(path, uri, s.config.IndexFiles...); filePath != "" {
|
|
|
|
|
|
return filePath, isDir
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return "", false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-13 00:43:36 +08:00
|
|
|
|
// 调用服务接口
|
2018-11-16 18:20:09 +08:00
|
|
|
|
func (s *Server) callServeHandler(h *handlerItem, r *Request) {
|
2017-12-26 10:13:49 +08:00
|
|
|
|
if h.faddr == nil {
|
|
|
|
|
|
c := reflect.New(h.ctype)
|
2019-01-13 00:43:36 +08:00
|
|
|
|
s.niceCallFunc(func() {
|
2019-01-04 15:43:56 +08:00
|
|
|
|
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r)})
|
|
|
|
|
|
})
|
2019-01-16 20:27:58 +08:00
|
|
|
|
if !r.IsExited() {
|
|
|
|
|
|
s.niceCallFunc(func() {
|
|
|
|
|
|
c.MethodByName(h.fname).Call(nil)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
if !r.IsExited() {
|
|
|
|
|
|
s.niceCallFunc(func() {
|
|
|
|
|
|
c.MethodByName("Shut").Call(nil)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2017-12-26 10:13:49 +08:00
|
|
|
|
} else {
|
2018-08-19 11:25:15 +08:00
|
|
|
|
if h.finit != nil {
|
2019-01-16 20:27:58 +08:00
|
|
|
|
s.niceCallFunc(func() {
|
|
|
|
|
|
h.finit(r)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
if !r.IsExited() {
|
|
|
|
|
|
s.niceCallFunc(func() {
|
|
|
|
|
|
h.faddr(r)
|
|
|
|
|
|
})
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
2019-01-16 20:27:58 +08:00
|
|
|
|
if h.fshut != nil && !r.IsExited() {
|
|
|
|
|
|
s.niceCallFunc(func() {
|
|
|
|
|
|
h.fshut(r)
|
|
|
|
|
|
})
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
2017-12-26 10:13:49 +08:00
|
|
|
|
}
|
2017-12-12 17:25:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-16 20:27:58 +08:00
|
|
|
|
// 友好地调用方法
|
2019-01-13 00:43:36 +08:00
|
|
|
|
func (s *Server) niceCallFunc(f func()) {
|
2019-01-04 15:43:56 +08:00
|
|
|
|
defer func() {
|
2019-01-16 20:27:58 +08:00
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
|
switch err {
|
|
|
|
|
|
case gEXCEPTION_EXIT: fallthrough
|
|
|
|
|
|
case gEXCEPTION_EXIT_ALL:
|
|
|
|
|
|
return
|
|
|
|
|
|
default:
|
|
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
2019-01-04 15:43:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
f()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-26 20:29:28 +08:00
|
|
|
|
// http server静态文件处理,path可以为相对路径也可以为绝对路径
|
2019-01-04 15:43:56 +08:00
|
|
|
|
func (s *Server) serveFile(r *Request, path string) {
|
2018-08-26 20:29:28 +08:00
|
|
|
|
f, err := os.Open(path)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
r.Response.WriteStatus(http.StatusForbidden)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
defer f.Close()
|
2017-11-23 10:21:28 +08:00
|
|
|
|
info, _ := f.Stat()
|
|
|
|
|
|
if info.IsDir() {
|
|
|
|
|
|
if s.config.IndexFolder {
|
2018-04-14 01:05:46 +08:00
|
|
|
|
s.listDir(r, f)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
} else {
|
2018-04-14 01:05:46 +08:00
|
|
|
|
r.Response.WriteStatus(http.StatusForbidden)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2018-04-20 11:45:47 +08:00
|
|
|
|
// 读取文件内容返回, no buffer
|
2019-01-24 13:50:26 +08:00
|
|
|
|
http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), f)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-17 11:17:02 +08:00
|
|
|
|
// 显示目录列表
|
2018-04-14 01:05:46 +08:00
|
|
|
|
func (s *Server)listDir(r *Request, f http.File) {
|
2018-11-17 11:17:02 +08:00
|
|
|
|
files, err := f.Readdir(-1)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
if err != nil {
|
2018-04-14 01:05:46 +08:00
|
|
|
|
r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory")
|
2017-11-23 10:21:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2018-11-17 11:17:02 +08:00
|
|
|
|
sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
|
2017-11-23 10:21:28 +08:00
|
|
|
|
|
2018-04-14 01:05:46 +08:00
|
|
|
|
r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
|
|
r.Response.Write("<pre>\n")
|
2018-11-23 09:20:45 +08:00
|
|
|
|
if r.URL.Path != "/" {
|
|
|
|
|
|
r.Response.Write(fmt.Sprint("<a href=\"..\">..</a>\n"))
|
|
|
|
|
|
}
|
2018-11-17 11:17:02 +08:00
|
|
|
|
for _, file := range files {
|
|
|
|
|
|
name := file.Name()
|
2018-11-23 09:20:45 +08:00
|
|
|
|
if file.IsDir() {
|
|
|
|
|
|
name += "/"
|
|
|
|
|
|
}
|
|
|
|
|
|
r.Response.Write(fmt.Sprintf("<a href=\"%s\">%s</a>\n", name, ghtml.SpecialChars(name)))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
2018-04-14 01:05:46 +08:00
|
|
|
|
r.Response.Write("</pre>\n")
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|