improve CORS feature for ghttp.Server

This commit is contained in:
John
2019-09-03 17:18:16 +08:00
parent e2906fba0b
commit cd00ac446b
10 changed files with 83 additions and 66 deletions

View File

@ -1,11 +1,20 @@
package main
import "github.com/gogf/gf/os/glog"
import (
"fmt"
func Test() {
}
"github.com/gogf/gf/encoding/gcompress"
"github.com/gogf/gf/os/gfile"
)
func main() {
glog.Line().Println("123")
fmt.Println(gfile.Basename("/dir/*"))
return
err := gcompress.ZipPath(
"/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other",
"/Users/john/Temp/test.zip",
)
if err != nil {
panic(err)
}
}

View File

@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/gogf/gf/util/gconv"
)
@ -216,7 +217,9 @@ func (md *Model) GroupBy(groupBy string) *Model {
// 链式操作order by
func (md *Model) OrderBy(orderBy string) *Model {
model := md.getModel()
model.orderBy = orderBy
array := strings.Split(orderBy, " ")
array[0] = md.db.quoteWord(array[0])
model.orderBy = strings.Join(array, " ")
return model
}

View File

@ -9,12 +9,13 @@ package gcompress
import (
"archive/zip"
"bytes"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/text/gstr"
"io"
"os"
"path/filepath"
"strings"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/text/gstr"
)
// ZipPath compresses <path> to <dest> using zip compressing algorithm.
@ -31,7 +32,7 @@ func ZipPath(path, dest string, prefix ...string) error {
// ZipPathWriter compresses <path> to <writer> using zip compressing algorithm.
// The unnecessary parameter <prefix> indicates the path prefix for zip file.
func ZipPathWriter(path string, writer io.Writer, prefix ...string) error {
pathRealPath, err := gfile.Search(path)
realPath, err := gfile.Search(path)
if err != nil {
return err
}
@ -45,8 +46,13 @@ func ZipPathWriter(path string, writer io.Writer, prefix ...string) error {
if len(prefix) > 0 {
headerPrefix = prefix[0]
}
headerPrefix = strings.Trim(headerPrefix, "\\/")
// If path is a directory, add it to the zip prefix.
if gfile.IsDir(realPath) {
headerPrefix = headerPrefix + "/" + gfile.Basename(realPath)
}
for _, file := range files {
err := zipFile(file, headerPrefix+gfile.Dir(file[len(pathRealPath):]), zipWriter)
err := zipFile(file, headerPrefix+gfile.Dir(file[len(realPath):]), zipWriter)
if err != nil {
return err
}

View File

@ -21,28 +21,29 @@ import (
// 请求对象
type Request struct {
*http.Request
Id int // 请求ID(当前Server对象唯一)
Server *Server // 请求关联的服务器对象
Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全)
Session *Session // 与当前请求绑定的Session对象(并发安全)
Response *Response // 对应请求的返回数据操作对象
Router *Router // 匹配到的路由对象
EnterTime int64 // 请求进入时间(微秒)
LeaveTime int64 // 请求完成时间(微秒)
Middleware *Middleware // 中间件功能调用对象
handlers []*handlerParsedItem // 请求执行服务函数列表(包含中间件、路由函数、钩子函数)
handlerIndex int // 当前执行函数的索引号
hasHookHandler bool // 是否注册有钩子函数(用于请求时提高钩子函数功能启用判断效率)
parsedGet bool // GET参数是否已经解析
parsedPost bool // POST参数是否已经解析
queryVars map[string][]string // GET参数
routerVars map[string][]string // 路由解析参数
exit bool // 是否退出当前请求流程执行
params map[string]interface{} // 开发者自定义参数(请求流程中有效)
parsedHost string // 解析过后不带端口号的服务器域名名称
clientIp string // 解析过后的客户端IP地址
rawContent []byte // 客户端提交的原始参数
isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求)
Id int // 请求ID(当前Server对象唯一)
Server *Server // 请求关联的服务器对象
Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全)
Session *Session // 与当前请求绑定的Session对象(并发安全)
Response *Response // 对应请求的返回数据操作对象
Router *Router // 匹配到的路由对象
EnterTime int64 // 请求进入时间(微秒)
LeaveTime int64 // 请求完成时间(微秒)
Middleware *Middleware // 中间件功能调用对象
handlers []*handlerParsedItem // 请求执行服务函数列表(包含中间件、路由函数、钩子函数)
handlerIndex int // 当前执行函数的索引号
hasHookHandler bool // 是否检索到钩子函数(用于请求时提高钩子函数功能启用判断效率)
hasServeHandler bool // 是否检索到服务函数
parsedGet bool // GET参数是否已经解析
parsedPost bool // POST参数是否已经解析
queryVars map[string][]string // GET参数
routerVars map[string][]string // 路由解析参数
exit bool // 是否退出当前请求流程执行
params map[string]interface{} // 开发者自定义参数(请求流程中有效)
parsedHost string // 解析过后不带端口号的服务器域名名称
clientIp string // 解析过后的客户端IP地址
rawContent []byte // 客户端提交的原始参数
isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求)
}
// 创建一个Request对象

View File

@ -24,7 +24,7 @@ func (m *Middleware) Next() {
}
item = m.request.handlers[m.request.handlerIndex]
m.request.handlerIndex++
// 通过中间件模式不执行钩子函数
// 中间件执行时不执行钩子函数,由另外的逻辑进行控制
if item.handler.itemType == gHANDLER_TYPE_HOOK {
continue
}

View File

@ -9,9 +9,9 @@ package ghttp
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gogf/gf/os/gres"
@ -81,7 +81,7 @@ func (r *Response) Writefln(format string, params ...interface{}) {
// 返回JSON
func (r *Response) WriteJson(content interface{}) error {
if b, err := gparser.VarToJson(content); err != nil {
if b, err := json.Marshal(content); err != nil {
return err
} else {
r.Header().Set("Content-Type", "application/json")
@ -92,7 +92,7 @@ func (r *Response) WriteJson(content interface{}) error {
// 返回JSONP
func (r *Response) WriteJsonP(content interface{}) error {
if b, err := gparser.VarToJson(content); err != nil {
if b, err := json.Marshal(content); err != nil {
return err
} else {
//r.Header().Set("Content-Type", "application/json")
@ -120,19 +120,6 @@ func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
return nil
}
// Deprecated, please use CORSDefault instead.
//
// (已废弃请使用CORSDefault)允许AJAX跨域访问.
func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods string, maxAge ...int) {
age := 3628800
if len(maxAge) > 0 {
age = maxAge[0]
}
r.Header().Set("Access-Control-Allow-Origin", allowOrigin)
r.Header().Set("Access-Control-Allow-Methods", allowMethods)
r.Header().Set("Access-Control-Max-Age", strconv.Itoa(age))
}
// 返回HTTP Code状态码
func (r *Response) WriteStatus(status int, content ...interface{}) {
if r.buffer.Len() == 0 {

View File

@ -8,6 +8,7 @@
package ghttp
import (
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
)
@ -24,12 +25,21 @@ type CORSOptions struct {
// 默认的CORS配置
func (r *Response) DefaultCORSOptions() CORSOptions {
return CORSOptions{
options := CORSOptions{
AllowOrigin: "*",
AllowMethods: HTTP_METHODS,
AllowCredentials: "true",
AllowHeaders: "Origin, X-Requested-With, Content-Type, Accept, Key",
MaxAge: 3628800,
}
if referer := r.request.Referer(); referer != "" {
if p := gstr.PosR(referer, "/", 6); p != -1 {
options.AllowOrigin = referer[:p]
} else {
options.AllowOrigin = referer
}
}
return options
}
// See https://www.w3.org/TR/cors/ .

View File

@ -109,7 +109,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
// 动态服务检索
if serveFile == nil || serveFile.dir {
request.handlers, request.hasHookHandler = s.getHandlersWithCache(request)
request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)
}
// 判断最终对该请求提供的服务方式
@ -126,7 +126,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
// 静态服务
s.serveFile(request, serveFile)
} else {
if len(request.handlers) > 0 {
if request.hasServeHandler {
// 动态服务
request.Middleware.Next()
} else {

View File

@ -19,28 +19,30 @@ import (
type handlerCacheItem struct {
parsedItems []*handlerParsedItem
hasHook bool
hasServe bool
}
// 查询请求处理方法.
// 内部带锁机制可以并发读但是不能并发写并且有缓存机制按照Host、Method、Path进行缓存.
func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*handlerParsedItem, hasHook bool) {
cacheKey := s.serveHandlerKey(r.Method, r.URL.Path, r.GetHost())
if v := s.serveCache.Get(cacheKey); v == nil {
parsedItems, hasHook = s.searchHandlers(r.Method, r.URL.Path, r.GetHost())
func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) {
value := s.serveCache.GetOrSetFunc(s.serveHandlerKey(r.Method, r.URL.Path, r.GetHost()), func() interface{} {
parsedItems, hasHook, hasServe = s.searchHandlers(r.Method, r.URL.Path, r.GetHost())
if parsedItems != nil {
s.serveCache.Set(cacheKey, &handlerCacheItem{parsedItems, hasHook}, s.config.RouterCacheExpire*1000)
return &handlerCacheItem{parsedItems, hasHook, hasServe}
}
} else {
item := v.(*handlerCacheItem)
return item.parsedItems, item.hasHook
return nil
}, s.config.RouterCacheExpire*1000)
if value != nil {
item := value.(*handlerCacheItem)
return item.parsedItems, item.hasHook, item.hasServe
}
return
}
// 路由注册方法检索,返回所有该路由的注册函数,构造成数组返回
func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*handlerParsedItem, hasHook bool) {
func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) {
if len(path) == 0 {
return nil, false
return nil, false, false
}
// 遍历检索的域名列表,优先遍历默认域名
domains := []string{gDEFAULT_DOMAIN}
@ -56,7 +58,6 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han
}
parsedItemList := glist.New()
lastMiddlewareItem := (*glist.Element)(nil)
isServeHandlerAdded := false
for _, domain := range domains {
p, ok := s.serveTree[domain]
if !ok {
@ -98,7 +99,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han
for e := lists[i].Front(); e != nil; e = e.Next() {
item := e.Value.(*handlerItem)
// 服务路由函数只能添加一次
if isServeHandlerAdded {
if hasServe {
switch item.itemType {
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
continue
@ -126,7 +127,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han
switch item.itemType {
// 服务路由函数只能添加一次
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
isServeHandlerAdded = true
hasServe = true
parsedItemList.PushBack(parsedItem)
// 中间件需要排序

View File

@ -185,7 +185,7 @@ func (l *Logger) SetPath(path string) error {
}
if !gfile.Exists(path) {
if err := gfile.Mkdir(path); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
//fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
return err
}
}