mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
Merge branch 'master' of https://gitee.com/johng/gf
This commit is contained in:
3
TODO
3
TODO
@ -19,6 +19,7 @@ ghttp.Server的Cookie及Session锁机制优化(去掉map锁机制);
|
||||
ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps);
|
||||
ghttp路由功能增加分组路由特性;
|
||||
解决glog串日志情况;
|
||||
ghttp增加返回数据压缩机制;
|
||||
|
||||
|
||||
DONE:
|
||||
@ -53,4 +54,4 @@ DONE:
|
||||
29. gdb增加查询缓存特性;
|
||||
30. gpage分页增加对自定义后缀的支持,如:2.html, 2.php等等;
|
||||
31. gvalid包增加struct tag的校验规则、自定义错误提示信息绑定的支持特性;
|
||||
|
||||
32. 增加文件缓存包,可根据fsnotify机制进行缓存更新;
|
||||
|
||||
@ -48,14 +48,14 @@ func (a *IntArray) Get(index int) int {
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Set(index int, value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.RUnlock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
}
|
||||
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Insert(index int, value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.RUnlock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
@ -65,14 +65,14 @@ func (a *IntArray) Insert(index int, value int) {
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Remove(index int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.RUnlock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
|
||||
// 追加数据项
|
||||
func (a *IntArray) Append(value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.RUnlock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = append(a.array, value)
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ func (a *IntArray) Slice() []int {
|
||||
// 清空数据数组
|
||||
func (a *IntArray) Clear() {
|
||||
a.mu.Lock()
|
||||
defer a.mu.RUnlock()
|
||||
defer a.mu.Unlock()
|
||||
if a.cap > 0 {
|
||||
a.array = make([]int, a.size, a.cap)
|
||||
} else {
|
||||
@ -148,6 +148,6 @@ func (a *IntArray) LockFunc(f func(array []int)) {
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.Unlock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
}
|
||||
@ -20,22 +20,23 @@ import (
|
||||
// 请求对象
|
||||
type Request struct {
|
||||
http.Request
|
||||
parsedGet *gtype.Bool // GET参数是否已经解析
|
||||
parsedPost *gtype.Bool // POST参数是否已经解析
|
||||
queryVars map[string][]string // GET参数
|
||||
routerVars map[string][]string // 路由解析参数
|
||||
exit *gtype.Bool // 是否退出当前请求流程执行
|
||||
Id int // 请求id(唯一)
|
||||
Server *Server // 请求关联的服务器对象
|
||||
Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全)
|
||||
Session *Session // 与当前请求绑定的Session对象(并发安全)
|
||||
Response *Response // 对应请求的返回数据操作对象
|
||||
Router *Router // 匹配到的路由对象
|
||||
EnterTime int64 // 请求进入时间(微秒)
|
||||
LeaveTime int64 // 请求完成时间(微秒)
|
||||
Param interface{} // 开发者自定义参数
|
||||
parsedHost *gtype.String // 解析过后不带端口号的服务器域名名称
|
||||
clientIp *gtype.String // 解析过后的客户端IP地址
|
||||
parsedGet *gtype.Bool // GET参数是否已经解析
|
||||
parsedPost *gtype.Bool // POST参数是否已经解析
|
||||
queryVars map[string][]string // GET参数
|
||||
routerVars map[string][]string // 路由解析参数
|
||||
exit *gtype.Bool // 是否退出当前请求流程执行
|
||||
Id int // 请求id(唯一)
|
||||
Server *Server // 请求关联的服务器对象
|
||||
Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全)
|
||||
Session *Session // 与当前请求绑定的Session对象(并发安全)
|
||||
Response *Response // 对应请求的返回数据操作对象
|
||||
Router *Router // 匹配到的路由对象
|
||||
EnterTime int64 // 请求进入时间(微秒)
|
||||
LeaveTime int64 // 请求完成时间(微秒)
|
||||
Param interface{} // 开发者自定义参数
|
||||
parsedHost *gtype.String // 解析过后不带端口号的服务器域名名称
|
||||
clientIp *gtype.String // 解析过后的客户端IP地址
|
||||
isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求)
|
||||
}
|
||||
|
||||
// 创建一个Request对象
|
||||
@ -153,6 +154,16 @@ func (r *Request) GetHost() string {
|
||||
return host
|
||||
}
|
||||
|
||||
// 判断是否为静态文件请求
|
||||
func (r *Request) IsFileRequest() bool {
|
||||
return r.isFileRequest
|
||||
}
|
||||
|
||||
// 判断是否为AJAX请求
|
||||
func (r *Request) IsAjaxRequest() bool {
|
||||
return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
|
||||
}
|
||||
|
||||
// 获取请求的客户端IP地址
|
||||
func (r *Request) GetClientIp() string {
|
||||
ip := r.clientIp.Val()
|
||||
|
||||
@ -68,7 +68,7 @@ func (r *Request) GetPostBool(key string, def ... bool) bool {
|
||||
func (r *Request) GetPostInt(key string, def ... int) int {
|
||||
value := r.GetPostString(key)
|
||||
if value != "" {
|
||||
return gconv.Int(value[0])
|
||||
return gconv.Int(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -79,7 +79,7 @@ func (r *Request) GetPostInt(key string, def ... int) int {
|
||||
func (r *Request) GetPostUint(key string, def ... uint) uint {
|
||||
value := r.GetPostString(key)
|
||||
if value != "" {
|
||||
return gconv.Uint(value[0])
|
||||
return gconv.Uint(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -90,7 +90,7 @@ func (r *Request) GetPostUint(key string, def ... uint) uint {
|
||||
func (r *Request) GetPostFloat32(key string, def ... float32) float32 {
|
||||
value := r.GetPostString(key)
|
||||
if value != "" {
|
||||
return gconv.Float32(value[0])
|
||||
return gconv.Float32(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -101,7 +101,7 @@ func (r *Request) GetPostFloat32(key string, def ... float32) float32 {
|
||||
func (r *Request) GetPostFloat64(key string, def ... float64) float64 {
|
||||
value := r.GetPostString(key)
|
||||
if value != "" {
|
||||
return gconv.Float64(value[0])
|
||||
return gconv.Float64(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
|
||||
@ -67,7 +67,7 @@ func (r *Request) GetQueryBool(key string, def ... bool) bool {
|
||||
func (r *Request) GetQueryInt(key string, def ... int) int {
|
||||
value := r.GetQueryString(key)
|
||||
if value != "" {
|
||||
return gconv.Int(value[0])
|
||||
return gconv.Int(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -78,7 +78,7 @@ func (r *Request) GetQueryInt(key string, def ... int) int {
|
||||
func (r *Request) GetQueryUint(key string, def ... uint) uint {
|
||||
value := r.GetQueryString(key)
|
||||
if value != "" {
|
||||
return gconv.Uint(value[0])
|
||||
return gconv.Uint(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -89,7 +89,7 @@ func (r *Request) GetQueryUint(key string, def ... uint) uint {
|
||||
func (r *Request) GetQueryFloat32(key string, def ... float32) float32 {
|
||||
value := r.GetQueryString(key)
|
||||
if value != "" {
|
||||
return gconv.Float32(value[0])
|
||||
return gconv.Float32(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -100,7 +100,7 @@ func (r *Request) GetQueryFloat32(key string, def ... float32) float32 {
|
||||
func (r *Request) GetQueryFloat64(key string, def ... float64) float64 {
|
||||
value := r.GetQueryString(key)
|
||||
if value != "" {
|
||||
return gconv.Float64(value[0])
|
||||
return gconv.Float64(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
|
||||
@ -39,7 +39,7 @@ func (r *Request) GetRequestString(key string, def ... string) string {
|
||||
func (r *Request) GetRequestBool(key string, def ... bool) bool {
|
||||
value := r.GetRequestString(key)
|
||||
if value != "" {
|
||||
return gconv.Bool(value[0])
|
||||
return gconv.Bool(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -50,7 +50,7 @@ func (r *Request) GetRequestBool(key string, def ... bool) bool {
|
||||
func (r *Request) GetRequestInt(key string, def ... int) int {
|
||||
value := r.GetRequestString(key)
|
||||
if value != "" {
|
||||
return gconv.Int(value[0])
|
||||
return gconv.Int(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -61,7 +61,7 @@ func (r *Request) GetRequestInt(key string, def ... int) int {
|
||||
func (r *Request) GetRequestUint(key string, def ... uint) uint {
|
||||
value := r.GetRequestString(key)
|
||||
if value != "" {
|
||||
return gconv.Uint(value[0])
|
||||
return gconv.Uint(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -72,7 +72,7 @@ func (r *Request) GetRequestUint(key string, def ... uint) uint {
|
||||
func (r *Request) GetRequestFloat32(key string, def ... float32) float32 {
|
||||
value := r.GetRequestString(key)
|
||||
if value != "" {
|
||||
return gconv.Float32(value[0])
|
||||
return gconv.Float32(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
@ -83,7 +83,7 @@ func (r *Request) GetRequestFloat32(key string, def ... float32) float32 {
|
||||
func (r *Request) GetRequestFloat64(key string, def ... float64) float64 {
|
||||
value := r.GetRequestString(key)
|
||||
if value != "" {
|
||||
return gconv.Float64(value[0])
|
||||
return gconv.Float64(value)
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"net/http"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/encoding/gparser"
|
||||
@ -17,46 +16,29 @@ import (
|
||||
"gitee.com/johng/gf/g/frame/gins"
|
||||
)
|
||||
|
||||
// 服务端请求返回对象
|
||||
// 服务端请求返回对象。
|
||||
// 注意该对象并没有实现http.ResponseWriter接口,而是依靠ghttp.ResponseWriter实现。
|
||||
type Response struct {
|
||||
ResponseWriter
|
||||
Server *Server
|
||||
Writer *ResponseWriter // io.Writer
|
||||
mu sync.RWMutex // 缓冲区互斥锁
|
||||
buffer []byte // 每个请求的返回数据缓冲区
|
||||
Writer *ResponseWriter // ResponseWriter的别名
|
||||
request *Request // 关联的Request请求对象
|
||||
}
|
||||
|
||||
// 自定义的ResponseWriter,用于写入流的控制
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
Status int // http status
|
||||
Length int // response length
|
||||
}
|
||||
|
||||
// 创建一个ghttp.Response对象指针
|
||||
func newResponse(s *Server, w http.ResponseWriter) *Response {
|
||||
r := &Response {
|
||||
Server : s,
|
||||
ResponseWriter : ResponseWriter{w, http.StatusOK, 0},
|
||||
ResponseWriter : ResponseWriter{
|
||||
ResponseWriter : w,
|
||||
Status : http.StatusOK,
|
||||
buffer : make([]byte, 0),
|
||||
},
|
||||
}
|
||||
r.Writer = &r.ResponseWriter
|
||||
return r
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法
|
||||
func (w *ResponseWriter) Write(buffer []byte) (int, error) {
|
||||
n, e := w.ResponseWriter.Write(buffer)
|
||||
w.Length += n
|
||||
return n, e
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法
|
||||
func (w *ResponseWriter) WriteHeader(code int) {
|
||||
w.Status = code
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// 返回信息,任何变量自动转换为bytes
|
||||
func (r *Response) Write(content ... interface{}) {
|
||||
if len(content) == 0 {
|
||||
@ -225,11 +207,6 @@ func (r *Response) ClearBuffer() {
|
||||
// 输出缓冲区数据到客户端
|
||||
func (r *Response) OutputBuffer() {
|
||||
r.Header().Set("Server", r.Server.config.ServerAgent)
|
||||
if len(r.buffer) > 0 {
|
||||
r.mu.Lock()
|
||||
r.ResponseWriter.Write(r.buffer)
|
||||
r.buffer = make([]byte, 0)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
//r.handleGzip()
|
||||
r.Writer.OutputBuffer()
|
||||
}
|
||||
|
||||
78
g/net/ghttp/ghttp_response_gzip.go
Normal file
78
g/net/ghttp/ghttp_response_gzip.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// 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,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
//
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/encoding/gcompress"
|
||||
"strings"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// 默认的gzip压缩文件类型
|
||||
var defaultGzipContentTypes = []string{
|
||||
"application/atom+xml",
|
||||
"application/font-sfnt",
|
||||
"application/javascript",
|
||||
"application/json",
|
||||
"application/ld+json",
|
||||
"application/manifest+json",
|
||||
"application/rdf+xml",
|
||||
"application/rss+xml",
|
||||
"application/schema+json",
|
||||
"application/vnd.geo+json",
|
||||
"application/vnd.ms-fontobject",
|
||||
"application/x-font-ttf",
|
||||
"application/x-javascript",
|
||||
"application/x-web-app-manifest+json",
|
||||
"application/xhtml+xml",
|
||||
"application/xml",
|
||||
"font/eot",
|
||||
"font/opentype",
|
||||
"image/bmp",
|
||||
"image/svg+xml",
|
||||
"image/vnd.microsoft.icon",
|
||||
"image/x-icon",
|
||||
"text/cache-manifest",
|
||||
"text/css",
|
||||
"text/html",
|
||||
"text/javascript",
|
||||
"text/plain",
|
||||
"text/vcard",
|
||||
"text/vnd.rim.location.xloc",
|
||||
"text/vtt",
|
||||
"text/x-component",
|
||||
"text/x-cross-domain-policy",
|
||||
"text/xml",
|
||||
}
|
||||
|
||||
// 返回内容gzip检查处理
|
||||
func (r *Response) handleGzip() {
|
||||
// 如果客户端支持gzip压缩,并且服务端设置开启gzip压缩特性,那么执行压缩
|
||||
encoding := r.request.Header.Get("Accept-Encoding")
|
||||
if encoding != "" && strings.Contains(encoding, "gzip") {
|
||||
mimeType := ""
|
||||
ext := gfile.Ext(r.request.URL.Path)
|
||||
if ext != "" {
|
||||
mimeType = strings.Split(mime.TypeByExtension(ext), ";")[0]
|
||||
}
|
||||
if mimeType == "" {
|
||||
contentType := r.Header().Get("Content-Type")
|
||||
if contentType != "" {
|
||||
mimeType = strings.Split(contentType, ";")[0]
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := r.Server.gzipMimesMap[mimeType]; ok {
|
||||
r.SetBuffer(gcompress.Gzip(r.buffer))
|
||||
r.Header().Set("Content-Length", gconv.String(len(r.buffer)))
|
||||
r.Header().Set("Content-Encoding", "gzip")
|
||||
}
|
||||
}
|
||||
}
|
||||
43
g/net/ghttp/ghttp_response_writer.go
Normal file
43
g/net/ghttp/ghttp_response_writer.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// 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,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
//
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 自定义的ResponseWriter,用于写入流的控制
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
mu sync.RWMutex // 缓冲区互斥锁
|
||||
Status int // http status
|
||||
buffer []byte // 缓冲区内容
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法
|
||||
func (w *ResponseWriter) Write(buffer []byte) (int, error) {
|
||||
w.buffer = append(w.buffer, buffer...)
|
||||
return len(buffer), nil
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法
|
||||
func (w *ResponseWriter) WriteHeader(code int) {
|
||||
w.Status = code
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// 输出buffer数据到客户端
|
||||
func (w *ResponseWriter) OutputBuffer() {
|
||||
if len(w.buffer) > 0 {
|
||||
w.mu.Lock()
|
||||
w.ResponseWriter.Write(w.buffer)
|
||||
w.buffer = make([]byte, 0)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ type Server struct {
|
||||
paths *gspath.SPath // 静态文件检索对象
|
||||
config ServerConfig // 配置对象
|
||||
servers []*gracefulServer // 底层http.Server列表
|
||||
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
|
||||
methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充)
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
// 服务注册相关
|
||||
@ -90,6 +90,7 @@ type Server struct {
|
||||
errorLogger *glog.Logger // error log日志对象
|
||||
// 其他属性
|
||||
nameToUriType *gtype.Int // 服务注册时对象和方法名称转换为URI时的规则
|
||||
gzipMimesMap map[string]struct{} // 支持gzip压缩的类型
|
||||
}
|
||||
|
||||
// 路由对象
|
||||
@ -181,7 +182,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
name : sname,
|
||||
paths : gspath.New(),
|
||||
servers : make([]*gracefulServer, 0),
|
||||
methodsMap : make(map[string]bool),
|
||||
methodsMap : make(map[string]struct{}),
|
||||
statusHandlerMap : make(map[string]HandlerFunc),
|
||||
serveTree : make(map[string]interface{}),
|
||||
hooksTree : make(map[string]interface{}),
|
||||
@ -203,6 +204,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
errorLogEnabled : gtype.NewBool(),
|
||||
logHandler : gtype.NewInterface(),
|
||||
nameToUriType : gtype.NewInt(),
|
||||
gzipMimesMap : make(map[string]struct{}),
|
||||
}
|
||||
s.errorLogger.SetBacktraceSkip(4)
|
||||
s.accessLogger.SetBacktraceSkip(4)
|
||||
@ -210,7 +212,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
s.serveCache.SetCap(gSERVE_CACHE_LRU_SIZE)
|
||||
s.hooksCache.SetCap(gHOOKS_CACHE_LRU_SIZE)
|
||||
for _, v := range strings.Split(gHTTP_METHODS, ",") {
|
||||
s.methodsMap[v] = true
|
||||
s.methodsMap[v] = struct{}{}
|
||||
}
|
||||
// 初始化时使用默认配置
|
||||
s.SetConfig(defaultServerConfig)
|
||||
@ -254,6 +256,12 @@ func (s *Server) Start() error {
|
||||
})
|
||||
}
|
||||
}
|
||||
// gzip压缩文件类型
|
||||
if s.config.GzipContentTypes != nil {
|
||||
for _, v := range s.config.GzipContentTypes {
|
||||
s.gzipMimesMap[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 启动http server
|
||||
reloaded := false
|
||||
|
||||
@ -59,27 +59,31 @@ type ServerConfig struct {
|
||||
AllowIps []string // 仅允许访问的ip列表,支持ip前缀过滤,如: 10 将仅允许10开头的ip访问
|
||||
// 路由访问控制
|
||||
DenyRoutes []string // 不允许访问的路由规则列表
|
||||
// Gzip压缩文件类型
|
||||
GzipContentTypes []string // 允许进行gzip压缩的文件类型
|
||||
}
|
||||
|
||||
// 默认HTTP Server
|
||||
var defaultServerConfig = ServerConfig {
|
||||
Addr : "",
|
||||
HTTPSAddr : "",
|
||||
Handler : nil,
|
||||
ReadTimeout : 60 * time.Second,
|
||||
WriteTimeout : 60 * time.Second,
|
||||
IdleTimeout : 60 * time.Second,
|
||||
MaxHeaderBytes : 1024,
|
||||
IndexFiles : []string{"index.html", "index.htm"},
|
||||
IndexFolder : false,
|
||||
ServerAgent : "gf",
|
||||
ServerRoot : "",
|
||||
Addr : "",
|
||||
HTTPSAddr : "",
|
||||
Handler : nil,
|
||||
ReadTimeout : 60 * time.Second,
|
||||
WriteTimeout : 60 * time.Second,
|
||||
IdleTimeout : 60 * time.Second,
|
||||
MaxHeaderBytes : 1024,
|
||||
IndexFiles : []string{"index.html", "index.htm"},
|
||||
IndexFolder : false,
|
||||
ServerAgent : "gf",
|
||||
ServerRoot : "",
|
||||
|
||||
CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE,
|
||||
SessionMaxAge : gDEFAULT_SESSION_MAX_AGE,
|
||||
SessionIdName : gDEFAULT_SESSION_ID_NAME,
|
||||
CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE,
|
||||
SessionMaxAge : gDEFAULT_SESSION_MAX_AGE,
|
||||
SessionIdName : gDEFAULT_SESSION_ID_NAME,
|
||||
|
||||
ErrorLogEnabled : true,
|
||||
ErrorLogEnabled : true,
|
||||
|
||||
GzipContentTypes : defaultGzipContentTypes,
|
||||
}
|
||||
|
||||
// 获取默认的http server设置
|
||||
@ -108,7 +112,10 @@ func (s *Server)SetConfig(c ServerConfig) {
|
||||
if s.config.ServerAgent == "" {
|
||||
s.SetServerAgent(defaultServerConfig.ServerAgent)
|
||||
}
|
||||
// 其他可动态设置的配置
|
||||
|
||||
// **********************
|
||||
// 可动态设置的配置处理
|
||||
// **********************
|
||||
s.SetLogPath(c.LogPath)
|
||||
s.SetLogHandler(c.LogHandler)
|
||||
s.SetErrorLogEnabled(c.ErrorLogEnabled)
|
||||
@ -124,7 +131,6 @@ func (s *Server)SetConfig(c ServerConfig) {
|
||||
s.SetSessionIdName(c.SessionIdName)
|
||||
}
|
||||
s.SetNameToUriType(c.NameToUriType)
|
||||
|
||||
}
|
||||
|
||||
// 设置http server参数 - Addr
|
||||
@ -283,6 +289,13 @@ func (s *Server) SetDenyRoutes(routes []string) {
|
||||
s.config.DenyRoutes = routes
|
||||
}
|
||||
|
||||
func (s *Server) SetGzipContentTypes(types []string) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error("cannot be changed while running")
|
||||
}
|
||||
s.config.GzipContentTypes = types
|
||||
}
|
||||
|
||||
// 设置http server参数 - CookieMaxAge
|
||||
func (s *Server)SetCookieMaxAge(maxage int) {
|
||||
s.cookieMaxAge.Set(maxage)
|
||||
|
||||
@ -52,28 +52,54 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
s.closeQueue.PushBack(request)
|
||||
}()
|
||||
|
||||
// 优先执行静态文件检索
|
||||
filePath := s.paths.Search(r.URL.Path)
|
||||
if filePath != "" {
|
||||
if gfile.IsDir(filePath) {
|
||||
// 如果是目录需要处理index files
|
||||
if len(s.config.IndexFiles) > 0 {
|
||||
for _, file := range s.config.IndexFiles {
|
||||
fpath := s.paths.Search(filePath + gfile.Separator + file)
|
||||
if fpath != "" {
|
||||
filePath = fpath
|
||||
request.isFileRequest = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request.isFileRequest = true
|
||||
}
|
||||
}
|
||||
|
||||
// 事件 - BeforeServe
|
||||
s.callHookHandler(HOOK_BEFORE_SERVE, request)
|
||||
|
||||
// 路由注册检索
|
||||
// 服务路由信息检索
|
||||
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)
|
||||
}
|
||||
if request.IsFileRequest() {
|
||||
// 如果存在请求的具体文件,那么优先服务文件
|
||||
s.serveFile(request, filePath)
|
||||
} else {
|
||||
handler = parsedItem.handler
|
||||
if request.Router == nil {
|
||||
for k, v := range parsedItem.values {
|
||||
request.routerVars[k] = v
|
||||
// 其次进行服务路由注册检索
|
||||
parsedItem := s.getServeHandlerWithCache(request)
|
||||
if parsedItem == nil {
|
||||
// 目录是优先级最低的操作
|
||||
if filePath != "" {
|
||||
s.serveFile(request, filePath)
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,20 +157,6 @@ func (s *Server)serveFile(r *Request, path string) {
|
||||
}
|
||||
info, _ := f.Stat()
|
||||
if info.IsDir() {
|
||||
// 处理index files
|
||||
if len(s.config.IndexFiles) > 0 {
|
||||
for _, file := range s.config.IndexFiles {
|
||||
// 这里使用s.paths来检索,而不是直接使用path,避免多目录检索情况
|
||||
// 例如:/tmp目录及源码目录都存在的情况按照优先级会返回/tmp,但是index files只在源码目录中存在
|
||||
fpath := s.paths.Search(r.URL.Path + gfile.Separator + file)
|
||||
if fpath != "" {
|
||||
f.Close()
|
||||
s.serveFile(r, fpath)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理访问目录
|
||||
if s.config.IndexFolder {
|
||||
s.listDir(r, f)
|
||||
} else {
|
||||
|
||||
@ -26,7 +26,7 @@ func (s *Server) handleAccessLog(r *Request) {
|
||||
content := fmt.Sprintf(`"%s %s %s %s" %s %s`,
|
||||
r.Method, r.Host, r.URL.String(), r.Proto,
|
||||
gconv.String(r.Response.Status),
|
||||
gconv.String(r.Response.Length),
|
||||
gconv.String(r.Response.BufferLength()),
|
||||
)
|
||||
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
|
||||
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
|
||||
|
||||
@ -25,7 +25,6 @@ type Config struct {
|
||||
name *gtype.String // 默认配置文件名称
|
||||
paths *gspath.SPath // 搜索目录路径
|
||||
jsons *gmap.StringInterfaceMap // 配置文件对象
|
||||
closed *gtype.Bool // 是否已经被close
|
||||
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率)
|
||||
}
|
||||
|
||||
@ -41,7 +40,6 @@ func New(path string, file...string) *Config {
|
||||
name : gtype.NewString(name),
|
||||
paths : s,
|
||||
jsons : gmap.NewStringInterfaceMap(),
|
||||
closed : gtype.NewBool(),
|
||||
vc : gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
@ -251,17 +249,14 @@ func (c *Config) Reload() {
|
||||
c.jsons.Clear()
|
||||
}
|
||||
|
||||
// 关闭Config对象,自动关闭异步协程
|
||||
func (c *Config) Close() {
|
||||
c.closed.Set(true)
|
||||
}
|
||||
|
||||
// 添加文件监控
|
||||
func (c *Config) addMonitor(path string) {
|
||||
if c.jsons.Get(path) == nil {
|
||||
gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
// 删除文件内容缓存,下一次查询会自动更新
|
||||
c.jsons.Remove(event.Path)
|
||||
})
|
||||
// 防止多goroutine同时调用
|
||||
if c.jsons.Get(path) != nil {
|
||||
return
|
||||
}
|
||||
gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
// 删除文件内容缓存,下一次查询会自动更新
|
||||
c.jsons.Remove(event.Path)
|
||||
})
|
||||
}
|
||||
|
||||
61
g/os/gfcache/gfcache.go
Normal file
61
g/os/gfcache/gfcache.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// 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,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// 文件缓存.
|
||||
package gfcache
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/os/gcache"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/os/gfsnotify"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
cap *gtype.Int // 缓存容量(byte),设置为0表示不限制
|
||||
size *gtype.Int // 缓存大小(Byte)
|
||||
cache *gcache.Cache // 缓存对象
|
||||
notify *gfsnotify.Watcher // 文件监控管理对象
|
||||
}
|
||||
|
||||
const (
|
||||
// 默认的缓存容量(不限制)
|
||||
gDEFAULT_CACHE_CAP = 0
|
||||
)
|
||||
|
||||
var (
|
||||
// 默认的文件缓存对象
|
||||
cache = New()
|
||||
)
|
||||
|
||||
func New(cap ... int) *Cache {
|
||||
c := gDEFAULT_CACHE_CAP
|
||||
if len(cap) > 0 {
|
||||
c = cap[0]
|
||||
}
|
||||
notify, _ := gfsnotify.New()
|
||||
return &Cache {
|
||||
cap : gtype.NewInt(c),
|
||||
size : gtype.NewInt(),
|
||||
cache : gcache.New(),
|
||||
notify : notify,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 获得已缓存的文件大小(byte)
|
||||
func GetSize() int {
|
||||
return cache.GetSize()
|
||||
}
|
||||
|
||||
// 获得文件内容 string
|
||||
func GetContents(path string) string {
|
||||
return cache.GetContents(path)
|
||||
}
|
||||
|
||||
// 获得文件内容 []byte
|
||||
func GetBinContents(path string) []byte {
|
||||
return cache.GetBinContents(path)
|
||||
}
|
||||
73
g/os/gfcache/gfcache_cache.go
Normal file
73
g/os/gfcache/gfcache_cache.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// 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,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// 文件缓存.
|
||||
package gfcache
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/os/gfsnotify"
|
||||
)
|
||||
|
||||
// 设置容量大小(MB)
|
||||
func (c *Cache) SetCap(cap int) {
|
||||
c.cap.Set(cap)
|
||||
}
|
||||
|
||||
// 获得缓存容量大小(byte)
|
||||
func (c *Cache) GetCap() int {
|
||||
return c.cap.Val()
|
||||
}
|
||||
|
||||
// 获得已缓存的文件大小(byte)
|
||||
func (c *Cache) GetSize() int {
|
||||
return c.size.Val()
|
||||
}
|
||||
|
||||
// 获得文件内容 string
|
||||
func (c *Cache) GetContents(path string) string {
|
||||
return string(c.GetBinContents(path))
|
||||
}
|
||||
|
||||
// 获得文件内容 []byte
|
||||
func (c *Cache) GetBinContents(path string) []byte {
|
||||
v := c.cache.Get(path)
|
||||
if v != nil {
|
||||
return v.([]byte)
|
||||
}
|
||||
b := gfile.GetBinContents(path)
|
||||
if b != nil && (c.cap.Val() == 0 || c.size.Val() < c.cap.Val()) {
|
||||
c.addMonitor(path)
|
||||
c.cache.Set(path, b, 0)
|
||||
c.size.Add(len(b))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 添加文件监控
|
||||
func (c *Cache) addMonitor(path string) {
|
||||
// 防止多goroutine同时调用
|
||||
if c.cache.Get(path) != nil {
|
||||
return
|
||||
}
|
||||
c.notify.Add(path, func(event *gfsnotify.Event) {
|
||||
r := c.cache.Get(path).([]byte)
|
||||
// 是否删除
|
||||
if event.IsRemove() {
|
||||
c.cache.Remove(path)
|
||||
c.size.Add(-len(r))
|
||||
}
|
||||
// 更新缓存内容
|
||||
if c.cap.Val() == 0 || c.size.Val() < c.cap.Val() {
|
||||
b := gfile.GetBinContents(path)
|
||||
if b != nil {
|
||||
dif := len(b) - len(r)
|
||||
c.cache.Set(path, b, 0)
|
||||
c.size.Add(dif)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -28,16 +28,19 @@ import (
|
||||
|
||||
// 封装了常用的文件操作方法,如需更详细的文件控制,请查看官方os包
|
||||
|
||||
|
||||
// 文件分隔符
|
||||
const (
|
||||
Separator = string(filepath.Separator)
|
||||
)
|
||||
|
||||
// 源码的main包所在目录,仅仅会设置一次
|
||||
var mainPkgPath = gtype.NewString()
|
||||
var (
|
||||
// 源码的main包所在目录,仅仅会设置一次
|
||||
mainPkgPath = gtype.NewString()
|
||||
|
||||
// 编译时的 GOROOT 数值
|
||||
var goRootOfBuild = gtype.NewString()
|
||||
// 编译时的 GOROOT 数值
|
||||
goRootOfBuild = gtype.NewString()
|
||||
)
|
||||
|
||||
// 给定文件的绝对路径创建文件
|
||||
func Mkdir(path string) error {
|
||||
|
||||
@ -70,12 +70,14 @@ func (p *Pool) File() (*File, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭指针池
|
||||
func (p *Pool) Close() {
|
||||
// 关闭指针池(返回error是标准库io.ReadWriteCloser接口实现)
|
||||
func (p *Pool) Close() error {
|
||||
p.pool.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获得底层文件指针
|
||||
func (f *File) Close() {
|
||||
// 获得底层文件指针(返回error是标准库io.ReadWriteCloser接口实现)
|
||||
func (f *File) Close() error {
|
||||
f.pool.pool.Put(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -12,12 +12,11 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"gitee.com/johng/gf/g/encoding/ghash"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/os/gfsnotify"
|
||||
"gitee.com/johng/gf/g/os/gspath"
|
||||
"gitee.com/johng/gf/g/os/gfcache"
|
||||
)
|
||||
|
||||
// 视图对象
|
||||
@ -25,7 +24,6 @@ type View struct {
|
||||
mu sync.RWMutex
|
||||
paths *gspath.SPath // 模板查找目录(绝对路径)
|
||||
funcmap map[string]interface{} // FuncMap
|
||||
contents *gmap.StringStringMap // 已解析的模板文件内容
|
||||
delimiters []string // 模板变量分隔符号
|
||||
}
|
||||
|
||||
@ -62,7 +60,6 @@ func New(path string) *View {
|
||||
view := &View {
|
||||
paths : s,
|
||||
funcmap : make(map[string]interface{}),
|
||||
contents : gmap.NewStringStringMap(),
|
||||
delimiters : make([]string, 2),
|
||||
}
|
||||
view.SetDelimiters("{{", "}}")
|
||||
@ -82,18 +79,12 @@ func (view *View) AddPath(path string) error {
|
||||
|
||||
// 解析模板,返回解析后的内容
|
||||
func (view *View) Parse(file string, params...map[string]interface{}) ([]byte, error) {
|
||||
path := view.paths.Search(file)
|
||||
content := view.contents.Get(path)
|
||||
if content == "" {
|
||||
content = gfile.GetContents(path)
|
||||
if content != "" {
|
||||
view.addMonitor(path)
|
||||
view.contents.Set(path, content)
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
path := view.paths.Search(file)
|
||||
if path == "" {
|
||||
return nil, errors.New("tpl \"" + file + "\" not found")
|
||||
}
|
||||
content := gfcache.GetContents(path)
|
||||
|
||||
// 模板参数
|
||||
data := (map[string]interface{})(nil)
|
||||
if len(params) > 0 {
|
||||
@ -154,13 +145,3 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) templa
|
||||
}
|
||||
return template.HTML(content)
|
||||
}
|
||||
|
||||
// 添加模板文件监控
|
||||
func (view *View) addMonitor(path string) {
|
||||
if view.contents.Get(path) == "" {
|
||||
gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
// 删除文件内容缓存,下一次查询会自动更新
|
||||
view.contents.Remove(event.Path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.SetIndexFolder(true)
|
||||
s.SetServerRoot("/home/john/Workspace/view")
|
||||
s.SetServerRoot("/home/john/Workspace/Go/gf-home/static/plugin/editor.md/css")
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
28
geg/os/gfcache/gfcache.go
Normal file
28
geg/os/gfcache/gfcache.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/os/gfcache"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := 0
|
||||
r := ""
|
||||
path := gfile.TempDir() + gfile.Separator + "temp"
|
||||
gfile.PutContents(path, "hello")
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
|
||||
gfile.PutContentsAppend(path, " john")
|
||||
|
||||
// 等待1秒以便gfsnotify回调能处理完成
|
||||
time.Sleep(time.Second)
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
}
|
||||
@ -1,11 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
methodMap := make(map[string]bool)
|
||||
methodMap["t"] = true
|
||||
fmt.Println(methodMap["t"])
|
||||
s := `<a href="baidu.com">百度</a>"`
|
||||
gregex.ReplaceStringFunc(`href="(.+?)"`, s, func(s string) string {
|
||||
fmt.Println(s)
|
||||
return s
|
||||
})
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "0.99 beta"
|
||||
const VERSION = "0.99.682 beta"
|
||||
const AUTHORS = "john<john@johng.cn>"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user