This commit is contained in:
john
2018-08-16 18:17:47 +08:00
parent 501e8e25ec
commit 2337b86815
11 changed files with 164 additions and 106 deletions

2
TODO
View File

@ -15,6 +15,8 @@ map转struct增加对tag的支持
gcache检查在i386下的int64->int转换问题
gfsnotify增加对于目录的监控
orm增加sqlite对Save方法的支持(去掉触发器语句);
ghttp.Server的Cookie及Session锁机制优化(去掉map锁机制);
ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps)
DONE:
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换

View File

@ -70,7 +70,7 @@ func (p *Pool) Get() (interface{}, error) {
for !p.closed.Val() {
if r := p.list.PopFront(); r != nil {
f := r.(*poolItem)
if f.expire > gtime.Millisecond() {
if f.expire == 0 || f.expire > gtime.Millisecond() {
return f.value, nil
}
} else {
@ -99,7 +99,7 @@ func (p *Pool) expireCheckingLoop() {
for {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
if item.expire > gtime.Millisecond() {
if item.expire == 0 || item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}

View File

@ -22,16 +22,22 @@ import (
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/container/gqueue"
"gitee.com/johng/gf/g/os/gspath"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/genv"
"github.com/gorilla/websocket"
"gitee.com/johng/gf/g/os/gtime"
"time"
"gitee.com/johng/gf/g/os/gfile"
)
const (
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
HOOK_BEFORE_SERVE = "BeforeServe"
HOOK_AFTER_SERVE = "AfterServe"
HOOK_BEFORE_OUTPUT = "BeforeOutput"
HOOK_AFTER_OUTPUT = "AfterOutput"
HOOK_BEFORE_CLOSE = "BeforeClose"
HOOK_AFTER_CLOSE = "AfterClose"
)
const (
gHTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
@ -208,8 +214,14 @@ func GetServer(name...interface{}) (*Server) {
// 作为守护协程异步执行(当同一进程中存在多个Web Server时需要采用这种方式执行)
// 需要结合Wait方式一起使用
func (s *Server) Start() error {
// 服务进程初始化,只会初始化一次
serverProcInit()
// 当前Web Server状态判断
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server is already running")
}
// 如果设置了静态文件目录,那么严格按照静态文件目录进行检索
// 否则默认使用当前可执行文件目录并且如果是开发环境默认也会添加main包的源码目录路径做为二级检索
if s.config.ServerRoot != "" {
@ -221,13 +233,19 @@ func (s *Server) Start() error {
}
}
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server is already running")
}
// 底层http server配置
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
}
// 不允许访问的路由注册
if s.config.DenyRoutes != nil {
for _, v := range s.config.DenyRoutes {
s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) {
r.Response.WriteStatus(403)
r.Exit()
})
}
}
// 启动http server
reloaded := false

View File

@ -9,11 +9,11 @@ package ghttp
import (
"time"
"errors"
"net/http"
"strconv"
"gitee.com/johng/gf/g/os/gfile"
"strings"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gfile"
)
const (
@ -28,21 +28,20 @@ const (
// HTTP Server 设置结构体,静态配置
type ServerConfig struct {
// 底层http对象配置
Addr string // 监听IP和端口监听本地所有IP使用":端口"(支持多个地址,使用","号分隔)
HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔)
HTTPSCertPath string // HTTPS证书文件路径
HTTPSKeyPath string // HTTPS签名文件路径
Handler http.Handler // 默认的处理函数
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int // 最大的header长度
Addr string // 监听IP和端口监听本地所有IP使用":端口"(支持多个地址,使用","号分隔)
HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔)
HTTPSCertPath string // HTTPS证书文件路径
HTTPSKeyPath string // HTTPS签名文件路径
Handler http.Handler // 默认的处理函数
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int // 最大的header长度
// 静态文件配置
IndexFiles []string // 默认访问的文件列表
IndexFolder bool // 如果访问目录是否显示目录列表
ServerAgent string // server agent
ServerRoot string // 服务器服务的本地目录根路径
IndexFiles []string // 默认访问的文件列表
IndexFolder bool // 如果访问目录是否显示目录列表
ServerAgent string // server agent
ServerRoot string // 服务器服务的本地目录根路径
// 日志配置
LogPath string // 存放日志的目录路径
LogHandler func(r *Request, error ... interface{}) // 自定义日志处理回调方法
@ -55,6 +54,11 @@ type ServerConfig struct {
SessionIdName string // SessionId名称
// 其他设置
NameToUriType int // 服务注册时对象和方法名称转换为URI时的规则
// ip访问控制
DenyIps []string // 不允许访问的ip列表支持ip前缀过滤如: 10 将不允许10开头的ip访问
AllowIps []string // 仅允许访问的ip列表支持ip前缀过滤如: 10 将仅允许10开头的ip访问
// 路由访问控制
DenyRoutes []string // 不允许访问的路由规则列表
}
// 默认HTTP Server
@ -85,9 +89,9 @@ func DefaultSetting() ServerConfig {
// http server setting设置
// 注意使用该方法进行http server配置时需要配置所有的配置项否则没有配置的属性将会默认变量为空
func (s *Server)SetConfig(c ServerConfig) error {
func (s *Server)SetConfig(c ServerConfig) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
if c.Handler == nil {
c.Handler = http.HandlerFunc(s.defaultHttpHandle)
@ -120,22 +124,21 @@ func (s *Server)SetConfig(c ServerConfig) error {
s.SetSessionIdName(c.SessionIdName)
}
s.SetNameToUriType(c.NameToUriType)
return nil
}
// 设置http server参数 - Addr
func (s *Server)SetAddr(addr string) error {
func (s *Server)SetAddr(addr string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.Addr = addr
return nil
}
// 设置http server参数 - Port
func (s *Server)SetPort(port...int) error {
func (s *Server)SetPort(port...int) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("config cannot be changed while running")
}
if len(port) > 0 {
s.config.Addr = ""
@ -146,22 +149,21 @@ func (s *Server)SetPort(port...int) error {
s.config.Addr += ":" + strconv.Itoa(v)
}
}
return nil
}
// 设置http server参数 - HTTPS Addr
func (s *Server)SetHTTPSAddr(addr string) error {
func (s *Server)SetHTTPSAddr(addr string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.HTTPSAddr = addr
return nil
}
// 设置http server参数 - HTTPS Port
func (s *Server)SetHTTPSPort(port...int) error {
func (s *Server)SetHTTPSPort(port...int) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
if len(port) > 0 {
s.config.HTTPSAddr = ""
@ -172,94 +174,113 @@ func (s *Server)SetHTTPSPort(port...int) error {
s.config.HTTPSAddr += ":" + strconv.Itoa(v)
}
}
return nil
}
// 开启HTTPS支持但是必须提供Cert和Key文件
func (s *Server)EnableHTTPS(certFile, keyFile string) error {
func (s *Server)EnableHTTPS(certFile, keyFile string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.HTTPSCertPath = certFile
s.config.HTTPSKeyPath = keyFile
return nil
}
// 设置http server参数 - ReadTimeout
func (s *Server)SetReadTimeout(t time.Duration) error {
func (s *Server)SetReadTimeout(t time.Duration) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.ReadTimeout = t
return nil
}
// 设置http server参数 - WriteTimeout
func (s *Server)SetWriteTimeout(t time.Duration) error {
func (s *Server)SetWriteTimeout(t time.Duration) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.WriteTimeout = t
return nil
}
// 设置http server参数 - IdleTimeout
func (s *Server)SetIdleTimeout(t time.Duration) error {
func (s *Server)SetIdleTimeout(t time.Duration) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.IdleTimeout = t
return nil
}
// 设置http server参数 - MaxHeaderBytes
func (s *Server)SetMaxHeaderBytes(b int) error {
func (s *Server)SetMaxHeaderBytes(b int) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.MaxHeaderBytes = b
return nil
}
// 设置http server参数 - IndexFiles默认展示文件index.html, index.htm
func (s *Server)SetIndexFiles(index []string) error {
func (s *Server)SetIndexFiles(index []string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.IndexFiles = index
return nil
}
// 允许展示访问目录的文件列表
func (s *Server)SetIndexFolder(index bool) error {
func (s *Server)SetIndexFolder(index bool) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.IndexFolder = index
return nil
}
// 设置http server参数 - ServerAgent
func (s *Server)SetServerAgent(agent string) error {
func (s *Server)SetServerAgent(agent string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
s.config.ServerAgent = agent
return nil
}
// 设置http server参数 - ServerRoot
func (s *Server)SetServerRoot(root string) error {
func (s *Server)SetServerRoot(root string) {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
glog.Error("cannot be changed while running")
}
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
path := gfile.RealPath(root)
if path == "" {
return errors.New("invalid root path \"" + root + "\"")
glog.Error("invalid root path \"" + root + "\"")
}
s.config.ServerRoot = strings.TrimRight(path, string(gfile.Separator))
return nil
}
func (s *Server) SetDenyIps(ips []string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error("cannot be changed while running")
}
s.config.DenyIps = ips
}
func (s *Server) SetAllowIps(ips []string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error("cannot be changed while running")
}
s.config.AllowIps = ips
}
func (s *Server) SetDenyRoutes(routes []string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error("cannot be changed while running")
}
s.config.DenyRoutes = routes
}
// 设置http server参数 - CookieMaxAge
@ -278,20 +299,19 @@ func (s *Server)SetSessionIdName(name string) {
}
// 设置日志目录
func (s *Server)SetLogPath(path string) error {
func (s *Server)SetLogPath(path string) {
if len(path) == 0 {
return nil
return
}
errorLogPath := strings.TrimRight(path, gfile.Separator) + gfile.Separator + "error"
accessLogPath := strings.TrimRight(path, gfile.Separator) + gfile.Separator + "access"
if err := s.accessLogger.SetPath(accessLogPath); err != nil {
return err
glog.Error(err)
}
if err := s.errorLogger.SetPath(errorLogPath); err != nil {
return err
glog.Error(err)
}
s.logPath.Set(path)
return nil
}
// 设置是否开启access log日志功能

View File

@ -48,7 +48,7 @@ func GetCookie(r *Request) *Cookie {
response : r.Response,
}
c.init()
c.server.cookies.Set(r.Id, c)
r.Server.cookies.Set(r.Id, c)
return c
}

View File

@ -52,55 +52,51 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
s.closeQueue.PushBack(request)
}()
// 事件 - BeforeServe
s.callHookHandler(HOOK_BEFORE_SERVE, request)
// 路由注册检索
handler := (*handlerItem)(nil)
parsedItem := s.getServeHandlerWithCache(request)
if parsedItem == nil {
// 如果路由不匹配,那么执行静态文件检索
path := s.paths.Search(r.URL.Path)
if path != "" {
s.serveFile(request, path)
} else {
request.Response.WriteStatus(http.StatusNotFound)
request.Response.OutputBuffer()
}
return
} else {
handler = parsedItem.handler
if request.Router == nil {
for k, v := range parsedItem.values {
request.routerVars[k] = v
handler := (*handlerItem)(nil)
if !request.exit.Val() {
parsedItem := s.getServeHandlerWithCache(request)
if parsedItem == nil {
// 如果路由不匹配,那么执行静态文件检索
path := s.paths.Search(r.URL.Path)
if path != "" {
s.serveFile(request, path)
} else {
request.Response.WriteStatus(http.StatusNotFound)
}
} else {
handler = parsedItem.handler
if request.Router == nil {
for k, v := range parsedItem.values {
request.routerVars[k] = v
}
request.Router = parsedItem.handler.router
}
request.Router = parsedItem.handler.router
}
}
// **********************************************
// 以下操作仅对路由控制有效,包括事件处理,不对静态文件有效
// **********************************************
// 事件 - BeforeServe
s.callHookHandler("BeforeServe", request)
// 执行回调控制器/执行对象/方法
if handler != nil {
s.callServeHandler(handler, request)
}
// 事件 - AfterServe
s.callHookHandler("AfterServe", request)
s.callHookHandler(HOOK_AFTER_SERVE, request)
// 设置请求完成时间
request.LeaveTime = gtime.Microsecond()
// 事件 - BeforeOutput
s.callHookHandler("BeforeOutput", request)
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
// 输出Cookie
request.Cookie.Output()
// 输出缓冲区
request.Response.OutputBuffer()
// 事件 - AfterOutput
s.callHookHandler("AfterOutput", request)
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 初始化控制器
@ -182,12 +178,12 @@ func (s *Server) startCloseQueueLoop() {
for {
if v := s.closeQueue.PopFront(); v != nil {
r := v.(*Request)
s.callHookHandler("BeforeClose", r)
s.callHookHandler(HOOK_BEFORE_CLOSE, r)
// 关闭当前会话的Cookie
r.Cookie.Close()
// 更新Session会话超时时间
r.Session.UpdateExpire()
s.callHookHandler("AfterClose", r)
s.callHookHandler(HOOK_AFTER_CLOSE, r)
}
}
}()

View File

@ -12,6 +12,7 @@ import (
"strings"
"container/list"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/util/gstr"
)
@ -98,7 +99,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
continue
}
// 判断是否模糊匹配规则
if gregex.IsMatchString(`^[:\*]|{[\w\.\-]+}`, v) {
if gregex.IsMatchString(`^[:\*]|\{[\w\.\-]+\}|\*`, v) {
v = "*fuzz"
// 由于是模糊规则,因此这里会有一个*list用以将后续的路由规则加进来
// 检索会从叶子节点的链表往根节点按照优先级进行检索
@ -201,7 +202,13 @@ func (s *Server) patternToRegRule(rule string) (regrule string, names []string)
regrule += `/{0,1}(.*)`
names = append(names, v[1:])
default:
s, _ := gregex.ReplaceStringFunc(`{[\w\.\-]+}`, v, func(s string) string {
// 特殊字符替换
v = gstr.ReplaceByMap(v, map[string]string{
`.` : `\.`,
`+` : `\+`,
`*` : `.*`,
})
s, _ := gregex.ReplaceStringFunc(`\{[\w\.\-]+\}`, v, func(s string) string {
names = append(names, s[1 : len(s) - 1])
return `([\w\.\-]+)`
})

View File

@ -42,6 +42,7 @@ func GetSession(r *Request) *Session {
data : gmap.NewStringInterfaceMap(),
server : s,
}
s.sessions.Set(sid, ses, s.GetSessionMaxAge())
return ses
}

View File

@ -472,9 +472,10 @@ func CheckStruct(st interface{}, rules map[string]string, msgs...map[string]inte
return CheckMap(params, rules, errMsgs)
}
// 检测单条数据的规则其中params参数为非必须参数可以传递所有的校验参数进来进行多参数对比(部分校验规则需要)
// msgs为自定义错误信息由于同一条数据的校验规则可能存在多条为方便调用参数类型支持string/map[string]string允许传递多个自定义的错误信息如果类型为string那么中间使用"|"符号分隔多个自定义错误
// values参数为表单联合校验参数对于需要联合校验规则有效required-*、same、different
// 检测单条数据的规则.
// val为校验数据可以为任意格式
// msgs为自定义错误信息由于同一条数据的校验规则可能存在多条为方便调用参数类型支持string/map[string]string允许传递多个自定义的错误信息如果类型为string那么中间使用"|"符号分隔多个自定义错误;
// params参数为表单联合校验参数对于需要联合校验的规则有效required-*、same、different
func Check(val interface{}, rules string, msgs interface{}, params...map[string]interface{}) map[string]string {
// 内部会将参数全部转换为字符串类型进行校验
value := strings.TrimSpace(gconv.String(val))

View File

@ -0,0 +1 @@
You're not supposed to view this

View File

@ -0,0 +1,12 @@
package main
import "gitee.com/johng/gf/g"
func main() {
s := g.Server()
s.SetDenyRoutes([]string{
"/config*",
})
s.SetPort(8299)
s.Run()
}