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-29 16:03:30 +08:00
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
package ghttp
|
|
|
|
|
|
|
|
|
|
import (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2019-12-04 10:03:03 +08:00
|
|
|
|
"github.com/gogf/gf/debug/gdebug"
|
2019-06-22 11:03:50 +08:00
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"reflect"
|
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2019-09-11 21:19:45 +08:00
|
|
|
|
"github.com/gogf/gf/os/gsession"
|
2019-08-22 21:04:30 +08:00
|
|
|
|
|
2019-07-29 21:01:19 +08:00
|
|
|
|
"github.com/gogf/gf/container/garray"
|
|
|
|
|
"github.com/gogf/gf/container/gmap"
|
|
|
|
|
"github.com/gogf/gf/container/gtype"
|
|
|
|
|
"github.com/gogf/gf/os/gcache"
|
|
|
|
|
"github.com/gogf/gf/os/genv"
|
|
|
|
|
"github.com/gogf/gf/os/gfile"
|
|
|
|
|
"github.com/gogf/gf/os/glog"
|
|
|
|
|
"github.com/gogf/gf/os/gproc"
|
|
|
|
|
"github.com/gogf/gf/os/gtimer"
|
|
|
|
|
"github.com/gogf/gf/text/gregex"
|
|
|
|
|
"github.com/gogf/gf/util/gconv"
|
2019-08-01 14:07:25 +08:00
|
|
|
|
"github.com/gorilla/websocket"
|
|
|
|
|
"github.com/olekukonko/tablewriter"
|
2017-11-23 10:21:28 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-11-19 21:49:43 +08:00
|
|
|
|
type (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// Server结构体
|
|
|
|
|
Server struct {
|
2019-07-22 15:10:40 +08:00
|
|
|
|
name string // 服务名称
|
|
|
|
|
config ServerConfig // 配置对象
|
|
|
|
|
servers []*gracefulServer // 底层http.Server列表
|
|
|
|
|
serverCount *gtype.Int // 底层http.Server数量
|
|
|
|
|
closeChan chan struct{} // 用以关闭事件通知的通道
|
|
|
|
|
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
|
|
|
|
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
|
|
|
|
serveCache *gcache.Cache // 服务注册路由内存缓存
|
|
|
|
|
routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
|
|
|
|
|
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
2019-09-11 21:19:45 +08:00
|
|
|
|
sessionManager *gsession.Manager // Session管理器
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 路由对象
|
|
|
|
|
Router struct {
|
|
|
|
|
Uri string // 注册时的pattern - uri
|
|
|
|
|
Method string // 注册时的pattern - method
|
|
|
|
|
Domain string // 注册时的pattern - domain
|
|
|
|
|
RegRule string // 路由规则解析后对应的正则表达式
|
|
|
|
|
RegNames []string // 路由规则解析后对应的变量名称数组
|
|
|
|
|
Priority int // 优先级,用于链表排序,值越大优先级越高
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Router item just for dumping.
|
|
|
|
|
RouterItem struct {
|
2019-12-10 12:28:55 +08:00
|
|
|
|
Server string
|
|
|
|
|
Address string
|
|
|
|
|
Domain string
|
|
|
|
|
Type int
|
|
|
|
|
Middleware string
|
|
|
|
|
Method string
|
|
|
|
|
Route string
|
|
|
|
|
Priority int
|
|
|
|
|
IsServiceHandler bool
|
|
|
|
|
handler *handlerItem
|
2019-12-04 13:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-29 14:27:09 +08:00
|
|
|
|
// 路由函数注册信息
|
2019-06-19 09:06:52 +08:00
|
|
|
|
handlerItem struct {
|
2019-12-04 10:03:03 +08:00
|
|
|
|
itemId int // 用于标识该注册函数的唯一性ID
|
|
|
|
|
itemName string // 注册的函数名称信息(用于路由信息打印)
|
|
|
|
|
itemType int // 注册函数类型(对象/函数/控制器/中间件/钩子函数)
|
|
|
|
|
itemFunc HandlerFunc // 函数内存地址(与以上两个参数二选一)
|
|
|
|
|
initFunc HandlerFunc // 初始化请求回调函数(对象注册方式下有效)
|
|
|
|
|
shutFunc HandlerFunc // 完成请求回调函数(对象注册方式下有效)
|
|
|
|
|
middleware []HandlerFunc // 绑定的中间件列表
|
|
|
|
|
ctrlInfo *handlerController // 控制器服务函数反射信息
|
|
|
|
|
hookName string // 钩子类型名称(注册函数类型为钩子函数下有效)
|
|
|
|
|
router *Router // 注册时绑定的路由对象
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据特定URL.Path解析后的路由检索结果项
|
|
|
|
|
handlerParsedItem struct {
|
2019-09-18 23:20:45 +08:00
|
|
|
|
handler *handlerItem // 路由注册项
|
|
|
|
|
values map[string]string // 特定URL.Path的Router解析参数
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-03 15:54:12 +08:00
|
|
|
|
// 控制器服务函数反射信息
|
|
|
|
|
handlerController struct {
|
|
|
|
|
name string // 方法名称
|
|
|
|
|
reflect reflect.Type // 控制器类型
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 已注册的路由项
|
|
|
|
|
registeredRouteItem struct {
|
|
|
|
|
file string // 文件路径及行数地址
|
|
|
|
|
handler *handlerItem // 路由注册项
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pattern与回调函数的绑定map
|
|
|
|
|
handlerMap = map[string]*handlerItem
|
|
|
|
|
|
|
|
|
|
// HTTP注册函数
|
|
|
|
|
HandlerFunc = func(r *Request)
|
|
|
|
|
|
|
|
|
|
// 文件描述符map
|
|
|
|
|
listenerFdMap = map[string]string
|
2018-11-19 21:49:43 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-08-01 13:04:15 +08:00
|
|
|
|
const (
|
2019-12-03 17:16:52 +08:00
|
|
|
|
SERVER_STATUS_STOPPED = 0
|
|
|
|
|
SERVER_STATUS_RUNNING = 1
|
|
|
|
|
HOOK_BEFORE_SERVE = "HOOK_BEFORE_SERVE"
|
|
|
|
|
HOOK_AFTER_SERVE = "HOOK_AFTER_SERVE"
|
|
|
|
|
HOOK_BEFORE_OUTPUT = "HOOK_BEFORE_OUTPUT"
|
|
|
|
|
HOOK_AFTER_OUTPUT = "HOOK_AFTER_OUTPUT"
|
2019-08-03 15:54:12 +08:00
|
|
|
|
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
|
|
|
|
gDEFAULT_SERVER = "default"
|
|
|
|
|
gDEFAULT_DOMAIN = "default"
|
|
|
|
|
gDEFAULT_METHOD = "ALL"
|
|
|
|
|
gHANDLER_TYPE_HANDLER = 1
|
|
|
|
|
gHANDLER_TYPE_OBJECT = 2
|
|
|
|
|
gHANDLER_TYPE_CONTROLLER = 3
|
|
|
|
|
gHANDLER_TYPE_MIDDLEWARE = 4
|
|
|
|
|
gHANDLER_TYPE_HOOK = 5
|
|
|
|
|
gEXCEPTION_EXIT = "exit"
|
|
|
|
|
gEXCEPTION_EXIT_ALL = "exit_all"
|
|
|
|
|
gEXCEPTION_EXIT_HOOK = "exit_hook"
|
2019-12-01 14:24:56 +08:00
|
|
|
|
gROUTE_CACHE_DURATION = time.Hour
|
2017-12-11 17:16:59 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-11-19 21:49:43 +08:00
|
|
|
|
var (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 所有支持的HTTP Method Map(初始化时自动填充),
|
|
|
|
|
// 用于快速检索需要
|
|
|
|
|
methodsMap = make(map[string]struct{})
|
|
|
|
|
|
|
|
|
|
// WebServer表,用以存储和检索名称与Server对象之间的关联关系
|
2019-07-23 23:20:27 +08:00
|
|
|
|
serverMapping = gmap.NewStrAnyMap(true)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
|
|
|
|
|
// 正常运行的WebServer数量,如果没有运行、失败或者全部退出,那么该值为0
|
|
|
|
|
serverRunning = gtype.NewInt()
|
|
|
|
|
|
|
|
|
|
// WebSocket默认配置
|
|
|
|
|
wsUpgrader = websocket.Upgrader{
|
|
|
|
|
// 默认允许WebSocket请求跨域,权限控制可以由业务层自己负责,灵活度更高
|
|
|
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
|
|
|
return true
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// WebServer已完成服务事件通道,当有事件时表示服务完成,当前进程退出
|
|
|
|
|
allDoneChan = make(chan struct{}, 1000)
|
|
|
|
|
|
|
|
|
|
// 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化)
|
|
|
|
|
serverProcessInited = gtype.NewBool()
|
|
|
|
|
|
|
|
|
|
// 是否开启WebServer平滑重启特性, 会开启额外的本地端口监听,用于进程管理通信(默认开启)
|
|
|
|
|
gracefulEnabled = true
|
2018-11-19 21:49:43 +08:00
|
|
|
|
)
|
2018-05-10 23:52:09 +08:00
|
|
|
|
|
2019-03-01 23:45:55 +08:00
|
|
|
|
func init() {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
for _, v := range strings.Split(HTTP_METHODS, ",") {
|
|
|
|
|
methodsMap[v] = struct{}{}
|
|
|
|
|
}
|
2019-03-01 23:45:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-05 11:38:36 +08:00
|
|
|
|
// 主要用于开发者在HTTP处理中自定义异常捕获时,判断捕获的异常是否Server抛出的自定义退出异常
|
|
|
|
|
func IsExitError(err interface{}) bool {
|
|
|
|
|
errStr := gconv.String(err)
|
|
|
|
|
if strings.EqualFold(errStr, gEXCEPTION_EXIT) ||
|
|
|
|
|
strings.EqualFold(errStr, gEXCEPTION_EXIT_ALL) ||
|
|
|
|
|
strings.EqualFold(errStr, gEXCEPTION_EXIT_HOOK) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-20 16:07:11 +08:00
|
|
|
|
// 是否开启平滑重启特性
|
|
|
|
|
func SetGraceful(enabled bool) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
gracefulEnabled = enabled
|
2019-02-20 16:07:11 +08:00
|
|
|
|
}
|
2018-07-31 22:55:54 +08:00
|
|
|
|
|
|
|
|
|
// Web Server进程初始化.
|
|
|
|
|
// 注意该方法不能放置于包初始化方法init中,不使用ghttp.Server的功能便不能初始化对应的协程goroutine逻辑.
|
2019-02-20 16:07:11 +08:00
|
|
|
|
func serverProcessInit() {
|
2019-11-07 16:40:34 +08:00
|
|
|
|
if !serverProcessInited.Cas(false, true) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// 如果是完整重启,那么需要等待主进程销毁后,才开始执行监听,防止端口冲突
|
|
|
|
|
if genv.Get(gADMIN_ACTION_RESTART_ENVKEY) != "" {
|
|
|
|
|
if p, e := os.FindProcess(gproc.PPid()); e == nil {
|
|
|
|
|
p.Kill()
|
|
|
|
|
p.Wait()
|
|
|
|
|
} else {
|
|
|
|
|
glog.Error(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 信号量管理操作监听
|
|
|
|
|
go handleProcessSignal()
|
|
|
|
|
// 异步监听进程间消息
|
|
|
|
|
if gracefulEnabled {
|
|
|
|
|
go handleProcessMessage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 是否处于开发环境,这里调用该方法初始化main包路径值,
|
|
|
|
|
// 防止异步服务goroutine获取main包路径失败,
|
|
|
|
|
// 该方法只有在main协程中才会执行。
|
|
|
|
|
gfile.MainPkgPath()
|
2018-05-10 23:52:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 10:42:59 +08:00
|
|
|
|
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
|
|
|
|
|
// 单例模式,请保证name的唯一性
|
2019-06-19 09:06:52 +08:00
|
|
|
|
func GetServer(name ...interface{}) *Server {
|
2019-09-04 19:23:19 +08:00
|
|
|
|
serverName := gDEFAULT_SERVER
|
2019-07-28 17:37:13 +08:00
|
|
|
|
if len(name) > 0 && name[0] != "" {
|
2019-09-04 19:23:19 +08:00
|
|
|
|
serverName = gconv.String(name[0])
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-09-04 19:23:19 +08:00
|
|
|
|
if s := serverMapping.Get(serverName); s != nil {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return s.(*Server)
|
|
|
|
|
}
|
2019-10-29 16:45:42 +08:00
|
|
|
|
c := defaultServerConfig
|
2019-06-19 09:06:52 +08:00
|
|
|
|
s := &Server{
|
2019-09-04 19:23:19 +08:00
|
|
|
|
name: serverName,
|
2019-06-19 09:06:52 +08:00
|
|
|
|
servers: make([]*gracefulServer, 0),
|
2019-11-28 23:19:37 +08:00
|
|
|
|
closeChan: make(chan struct{}, 10000),
|
2019-06-19 09:06:52 +08:00
|
|
|
|
serverCount: gtype.NewInt(),
|
|
|
|
|
statusHandlerMap: make(map[string]HandlerFunc),
|
|
|
|
|
serveTree: make(map[string]interface{}),
|
|
|
|
|
serveCache: gcache.New(),
|
|
|
|
|
routesMap: make(map[string][]registeredRouteItem),
|
|
|
|
|
servedCount: gtype.NewInt(),
|
|
|
|
|
}
|
|
|
|
|
// 初始化时使用默认配置
|
2019-11-07 11:32:25 +08:00
|
|
|
|
if err := s.SetConfig(c); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 记录到全局ServerMap中
|
2019-09-04 19:23:19 +08:00
|
|
|
|
serverMapping.Set(serverName, s)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return s
|
2017-12-07 14:57:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 23:57:20 +08:00
|
|
|
|
// 作为守护协程异步执行(当同一进程中存在多个Web Server时,需要采用这种方式执行),
|
|
|
|
|
// 需要结合Wait方式一起使用.
|
2018-05-13 14:04:37 +08:00
|
|
|
|
func (s *Server) Start() error {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Register group routes.
|
2019-08-06 20:40:04 +08:00
|
|
|
|
s.handlePreBindItems()
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Server process initialization, which can only be initialized once.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
serverProcessInit()
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Server can only be run once.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if s.Status() == SERVER_STATUS_RUNNING {
|
2019-11-08 19:52:49 +08:00
|
|
|
|
return errors.New("[ghttp] server is already running")
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// If there's no route registered and no static service enabled,
|
|
|
|
|
// it then returns an error of invalid usage of server.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
|
2019-11-08 19:52:49 +08:00
|
|
|
|
return errors.New(`[ghttp] there's no route set or static feature enabled, did you forget import the router?`)
|
|
|
|
|
}
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Logging path setting check.
|
2019-11-08 19:52:49 +08:00
|
|
|
|
if s.config.LogPath != "" {
|
|
|
|
|
if err := s.config.Logger.SetPath(s.config.LogPath); err != nil {
|
|
|
|
|
return errors.New(fmt.Sprintf("[ghttp] set log path '%s' error: %v", s.config.LogPath, err))
|
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-10-29 16:45:42 +08:00
|
|
|
|
// Default session storage.
|
|
|
|
|
if s.config.SessionStorage == nil {
|
2019-11-07 20:42:13 +08:00
|
|
|
|
path := ""
|
|
|
|
|
if s.config.SessionPath != "" {
|
|
|
|
|
path = gfile.Join(s.config.SessionPath, s.name)
|
|
|
|
|
if !gfile.Exists(path) {
|
|
|
|
|
if err := gfile.Mkdir(path); err != nil {
|
2019-11-08 19:52:49 +08:00
|
|
|
|
return errors.New(fmt.Sprintf("[ghttp] mkdir failed for '%s': %v", path, err))
|
2019-11-07 20:42:13 +08:00
|
|
|
|
}
|
2019-11-07 20:36:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.config.SessionStorage = gsession.NewStorageFile(path)
|
2019-10-29 16:45:42 +08:00
|
|
|
|
}
|
|
|
|
|
// Initialize session manager when start running.
|
2019-11-07 20:36:33 +08:00
|
|
|
|
s.sessionManager = gsession.New(
|
|
|
|
|
s.config.SessionMaxAge,
|
|
|
|
|
s.config.SessionStorage,
|
|
|
|
|
)
|
2019-10-29 16:45:42 +08:00
|
|
|
|
|
2019-11-07 16:40:34 +08:00
|
|
|
|
// PProf feature.
|
|
|
|
|
if s.config.PProfEnabled {
|
|
|
|
|
s.EnablePProf(s.config.PProfPattern)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default HTTP handler.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if s.config.Handler == nil {
|
2019-11-28 23:19:37 +08:00
|
|
|
|
s.config.Handler = http.HandlerFunc(s.defaultHandler)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-07 16:40:34 +08:00
|
|
|
|
// Start the HTTP server.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
reloaded := false
|
|
|
|
|
fdMapStr := genv.Get(gADMIN_ACTION_RELOAD_ENVKEY)
|
|
|
|
|
if len(fdMapStr) > 0 {
|
|
|
|
|
sfm := bufferToServerFdMap([]byte(fdMapStr))
|
|
|
|
|
if v, ok := sfm[s.name]; ok {
|
|
|
|
|
s.startServer(v)
|
|
|
|
|
reloaded = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !reloaded {
|
|
|
|
|
s.startServer(nil)
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// If this is a child process, it then notifies its parent exit.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if gproc.IsChild() {
|
|
|
|
|
gtimer.SetTimeout(2*time.Second, func() {
|
|
|
|
|
if err := gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP); err != nil {
|
2019-12-09 21:53:44 +08:00
|
|
|
|
//glog.Error("[ghttp] server error in process communication:", err)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 12:28:55 +08:00
|
|
|
|
s.dumpRouterMap()
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return nil
|
2018-05-13 14:04:37 +08:00
|
|
|
|
}
|
2018-04-27 22:12:11 +08:00
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// DumpRouterMap dumps the router map to the log.
|
2019-12-10 12:28:55 +08:00
|
|
|
|
func (s *Server) dumpRouterMap() {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
if s.config.DumpRouterMap && len(s.routesMap) > 0 {
|
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
|
|
|
|
table := tablewriter.NewWriter(buffer)
|
|
|
|
|
table.SetHeader([]string{"SERVER", "ADDRESS", "DOMAIN", "METHOD", "P", "ROUTE", "HANDLER", "MIDDLEWARE"})
|
|
|
|
|
table.SetRowLine(true)
|
|
|
|
|
table.SetBorder(false)
|
|
|
|
|
table.SetCenterSeparator("|")
|
|
|
|
|
table.SetColumnAlignment([]int{
|
|
|
|
|
tablewriter.ALIGN_CENTER,
|
|
|
|
|
tablewriter.ALIGN_CENTER,
|
|
|
|
|
tablewriter.ALIGN_CENTER,
|
|
|
|
|
tablewriter.ALIGN_CENTER,
|
|
|
|
|
tablewriter.ALIGN_CENTER,
|
|
|
|
|
tablewriter.ALIGN_LEFT,
|
|
|
|
|
tablewriter.ALIGN_LEFT,
|
|
|
|
|
tablewriter.ALIGN_LEFT,
|
|
|
|
|
})
|
2018-10-17 16:56:50 +08:00
|
|
|
|
|
2019-12-10 12:28:55 +08:00
|
|
|
|
for _, item := range s.GetRouterArray() {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
data := make([]string, 8)
|
2019-12-10 12:28:55 +08:00
|
|
|
|
data[0] = item.Server
|
|
|
|
|
data[1] = item.Address
|
|
|
|
|
data[2] = item.Domain
|
|
|
|
|
data[3] = item.Method
|
|
|
|
|
data[4] = gconv.String(len(strings.Split(item.Route, "/")) - 1 + item.Priority)
|
|
|
|
|
data[5] = item.Route
|
|
|
|
|
data[6] = item.handler.itemName
|
|
|
|
|
data[7] = item.Middleware
|
|
|
|
|
table.Append(data)
|
2019-12-04 13:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
table.Render()
|
|
|
|
|
s.config.Logger.Header(false).Printf("\n%s", buffer.String())
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-12-04 13:55:08 +08:00
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
|
2019-12-10 12:28:55 +08:00
|
|
|
|
// GetRouterArray retrieves and returns the router array.
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// The key of the returned map is the domain of the server.
|
2019-12-10 12:28:55 +08:00
|
|
|
|
func (s *Server) GetRouterArray() []RouterItem {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
m := make(map[string]*garray.SortedArray)
|
2019-12-09 21:53:44 +08:00
|
|
|
|
address := s.config.Address
|
|
|
|
|
if s.config.HTTPSAddr != "" {
|
|
|
|
|
if len(address) > 0 {
|
|
|
|
|
address += ","
|
|
|
|
|
}
|
|
|
|
|
address += "tls" + s.config.HTTPSAddr
|
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
for k, registeredItems := range s.routesMap {
|
|
|
|
|
array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k)
|
|
|
|
|
for index, registeredItem := range registeredItems {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
item := RouterItem{
|
2019-12-09 21:53:44 +08:00
|
|
|
|
Server: s.name,
|
|
|
|
|
Address: address,
|
|
|
|
|
Domain: array[4],
|
2019-12-08 22:55:32 +08:00
|
|
|
|
Type: registeredItem.handler.itemType,
|
2019-12-04 13:55:08 +08:00
|
|
|
|
Middleware: array[1],
|
|
|
|
|
Method: array[2],
|
|
|
|
|
Route: array[3],
|
|
|
|
|
Priority: len(registeredItems) - index - 1,
|
2019-08-03 23:57:20 +08:00
|
|
|
|
handler: registeredItem.handler,
|
|
|
|
|
}
|
2019-12-09 23:19:39 +08:00
|
|
|
|
switch item.handler.itemType {
|
|
|
|
|
case gHANDLER_TYPE_CONTROLLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_HANDLER:
|
|
|
|
|
item.IsServiceHandler = true
|
|
|
|
|
case gHANDLER_TYPE_MIDDLEWARE:
|
2019-12-04 13:55:08 +08:00
|
|
|
|
item.Middleware = "GLOBAL MIDDLEWARE"
|
2019-12-04 10:03:03 +08:00
|
|
|
|
}
|
|
|
|
|
if len(item.handler.middleware) > 0 {
|
|
|
|
|
for _, v := range item.handler.middleware {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
if item.Middleware != "" {
|
|
|
|
|
item.Middleware += ","
|
2019-12-04 10:03:03 +08:00
|
|
|
|
}
|
2019-12-04 13:55:08 +08:00
|
|
|
|
item.Middleware += gdebug.FuncName(v)
|
2019-12-04 10:03:03 +08:00
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-12-04 10:03:03 +08:00
|
|
|
|
// If the domain does not exist in the dump map, it create the map.
|
|
|
|
|
// The value of the map is a custom sorted array.
|
2019-12-04 13:55:08 +08:00
|
|
|
|
if _, ok := m[item.Domain]; !ok {
|
2019-12-04 10:03:03 +08:00
|
|
|
|
// Sort in ASC order.
|
2019-12-10 12:28:55 +08:00
|
|
|
|
m[item.Domain] = garray.NewSortedArray(func(v1, v2 interface{}) int {
|
2019-12-04 13:55:08 +08:00
|
|
|
|
item1 := v1.(RouterItem)
|
|
|
|
|
item2 := v2.(RouterItem)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
r := 0
|
2019-12-04 13:55:08 +08:00
|
|
|
|
if r = strings.Compare(item1.Domain, item2.Domain); r == 0 {
|
|
|
|
|
if r = strings.Compare(item1.Route, item2.Route); r == 0 {
|
|
|
|
|
if r = strings.Compare(item1.Method, item2.Method); r == 0 {
|
2019-08-03 23:57:20 +08:00
|
|
|
|
if item1.handler.itemType == gHANDLER_TYPE_MIDDLEWARE && item2.handler.itemType != gHANDLER_TYPE_MIDDLEWARE {
|
|
|
|
|
return -1
|
|
|
|
|
} else if item1.handler.itemType == gHANDLER_TYPE_MIDDLEWARE && item2.handler.itemType == gHANDLER_TYPE_MIDDLEWARE {
|
|
|
|
|
return 1
|
2019-12-04 13:55:08 +08:00
|
|
|
|
} else if r = strings.Compare(item1.Middleware, item2.Middleware); r == 0 {
|
|
|
|
|
r = item2.Priority - item1.Priority
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return r
|
2019-07-23 23:20:27 +08:00
|
|
|
|
})
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-12-04 13:55:08 +08:00
|
|
|
|
m[item.Domain].Add(item)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-10 12:28:55 +08:00
|
|
|
|
routerArray := make([]RouterItem, 0, 128)
|
|
|
|
|
for _, array := range m {
|
|
|
|
|
for _, v := range array.Slice() {
|
|
|
|
|
routerArray = append(routerArray, v.(RouterItem))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-10 12:28:55 +08:00
|
|
|
|
return routerArray
|
2018-10-17 10:09:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Run starts server listening in blocking way.
|
2019-06-22 11:03:50 +08:00
|
|
|
|
func (s *Server) Run() {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if err := s.Start(); err != nil {
|
2019-06-22 11:03:50 +08:00
|
|
|
|
glog.Fatal(err)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-12-04 13:55:08 +08:00
|
|
|
|
|
|
|
|
|
// Blocking using channel.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
<-s.closeChan
|
|
|
|
|
|
2019-11-08 19:52:49 +08:00
|
|
|
|
glog.Printf("[ghttp] %d: all servers shutdown", gproc.Pid())
|
2018-05-10 19:16:41 +08:00
|
|
|
|
}
|
2018-05-10 17:48:47 +08:00
|
|
|
|
|
2019-12-04 13:55:08 +08:00
|
|
|
|
// Wait blocks to wait for all servers done.
|
|
|
|
|
// It's commonly used in multiple servers situation.
|
2018-05-13 14:04:37 +08:00
|
|
|
|
func Wait() {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
<-allDoneChan
|
2018-06-01 00:11:45 +08:00
|
|
|
|
|
2019-11-08 19:52:49 +08:00
|
|
|
|
glog.Printf("[ghttp] %d: all servers shutdown", gproc.Pid())
|
2018-05-13 14:04:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 19:16:41 +08:00
|
|
|
|
// 开启底层Web Server执行
|
2018-05-10 23:52:09 +08:00
|
|
|
|
func (s *Server) startServer(fdMap listenerFdMap) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
var httpsEnabled bool
|
|
|
|
|
// 判断是否启用HTTPS
|
2019-11-07 16:40:34 +08:00
|
|
|
|
if s.config.TLSConfig != nil || (s.config.HTTPSCertPath != "" && s.config.HTTPSKeyPath != "") {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// ================
|
|
|
|
|
// HTTPS
|
|
|
|
|
// ================
|
|
|
|
|
if len(s.config.HTTPSAddr) == 0 {
|
2019-11-07 11:32:25 +08:00
|
|
|
|
if len(s.config.Address) > 0 {
|
|
|
|
|
s.config.HTTPSAddr = s.config.Address
|
|
|
|
|
s.config.Address = ""
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
|
|
|
|
s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
httpsEnabled = len(s.config.HTTPSAddr) > 0
|
|
|
|
|
var array []string
|
|
|
|
|
if v, ok := fdMap["https"]; ok && len(v) > 0 {
|
|
|
|
|
array = strings.Split(v, ",")
|
|
|
|
|
} else {
|
|
|
|
|
array = strings.Split(s.config.HTTPSAddr, ",")
|
|
|
|
|
}
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fd := 0
|
2019-08-03 15:54:12 +08:00
|
|
|
|
itemFunc := v
|
2019-06-19 09:06:52 +08:00
|
|
|
|
array := strings.Split(v, "#")
|
|
|
|
|
if len(array) > 1 {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
itemFunc = array[0]
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
|
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
|
fd = gconv.Int(array[1])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if fd > 0 {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(itemFunc))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
s.servers[len(s.servers)-1].isHttps = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// ================
|
|
|
|
|
// HTTP
|
|
|
|
|
// ================
|
|
|
|
|
// 当HTTPS服务未启用时,默认HTTP地址才会生效
|
2019-11-07 11:32:25 +08:00
|
|
|
|
if !httpsEnabled && len(s.config.Address) == 0 {
|
|
|
|
|
s.config.Address = gDEFAULT_HTTP_ADDR
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
var array []string
|
|
|
|
|
if v, ok := fdMap["http"]; ok && len(v) > 0 {
|
|
|
|
|
array = strings.Split(v, ",")
|
|
|
|
|
} else {
|
2019-11-07 11:32:25 +08:00
|
|
|
|
array = strings.Split(s.config.Address, ",")
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fd := 0
|
2019-08-03 15:54:12 +08:00
|
|
|
|
itemFunc := v
|
2019-06-19 09:06:52 +08:00
|
|
|
|
array := strings.Split(v, "#")
|
|
|
|
|
if len(array) > 1 {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
itemFunc = array[0]
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
|
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
|
fd = gconv.Int(array[1])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if fd > 0 {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(itemFunc))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 开始执行异步监听
|
|
|
|
|
serverRunning.Add(1)
|
|
|
|
|
for _, v := range s.servers {
|
|
|
|
|
go func(server *gracefulServer) {
|
|
|
|
|
s.serverCount.Add(1)
|
|
|
|
|
err := (error)(nil)
|
|
|
|
|
if server.isHttps {
|
2019-11-07 16:40:34 +08:00
|
|
|
|
err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath, s.config.TLSConfig)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
|
|
|
|
err = server.ListenAndServe()
|
|
|
|
|
}
|
|
|
|
|
// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
|
|
|
|
|
if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
|
|
|
|
|
glog.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
// 如果所有异步的http.Server都已经停止,那么WebServer就可以退出了
|
|
|
|
|
if s.serverCount.Add(-1) < 1 {
|
|
|
|
|
s.closeChan <- struct{}{}
|
|
|
|
|
// 如果所有WebServer都退出,那么退出Wait等待
|
|
|
|
|
if serverRunning.Add(-1) < 1 {
|
|
|
|
|
serverMapping.Remove(s.name)
|
|
|
|
|
allDoneChan <- struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}(v)
|
|
|
|
|
}
|
2018-08-01 13:04:15 +08:00
|
|
|
|
}
|
2018-04-23 19:22:59 +08:00
|
|
|
|
|
2018-08-01 13:04:15 +08:00
|
|
|
|
// 获取当前服务器的状态
|
|
|
|
|
func (s *Server) Status() int {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 当全局运行的Web Server数量为0时表示所有Server都是停止状态
|
|
|
|
|
if serverRunning.Val() == 0 {
|
|
|
|
|
return SERVER_STATUS_STOPPED
|
|
|
|
|
}
|
|
|
|
|
// 只要有一个Server处于运行状态,那么都表示运行状态
|
|
|
|
|
for _, v := range s.servers {
|
|
|
|
|
if v.status == SERVER_STATUS_RUNNING {
|
|
|
|
|
return SERVER_STATUS_RUNNING
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return SERVER_STATUS_STOPPED
|
2018-05-08 23:38:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 19:16:41 +08:00
|
|
|
|
// 获取当前监听的文件描述符信息,构造成map返回
|
2018-05-10 23:52:09 +08:00
|
|
|
|
func (s *Server) getListenerFdMap() map[string]string {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
m := map[string]string{
|
|
|
|
|
"https": "",
|
|
|
|
|
"http": "",
|
|
|
|
|
}
|
|
|
|
|
// s.servers是从HTTPS到HTTP优先级遍历,解析的时候也应当按照这个顺序读取fd
|
|
|
|
|
for _, v := range s.servers {
|
2019-08-03 15:54:12 +08:00
|
|
|
|
str := v.itemFunc + "#" + gconv.String(v.Fd()) + ","
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if v.isHttps {
|
|
|
|
|
m["https"] += str
|
|
|
|
|
} else {
|
|
|
|
|
m["http"] += str
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 去掉末尾的","号
|
|
|
|
|
if len(m["https"]) > 0 {
|
|
|
|
|
m["https"] = m["https"][0 : len(m["https"])-1]
|
|
|
|
|
}
|
|
|
|
|
if len(m["http"]) > 0 {
|
|
|
|
|
m["http"] = m["http"][0 : len(m["http"])-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m
|
2018-05-10 19:16:41 +08:00
|
|
|
|
}
|