comment updates for package ghttp

This commit is contained in:
john
2020-05-07 23:05:33 +08:00
parent edb745ed92
commit 5d21148657
10 changed files with 126 additions and 96 deletions

View File

@ -35,6 +35,10 @@ var (
//
// The parameter <pointer> can be type of: *struct/**struct/*[]struct/*[]*struct.
//
// It supports single and multiple struct convertion:
// 1. Single struct, post content like: {"id":1, "name":"john"}
// 2. Multiple struct, post content like: [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
//
// TODO: Improve the performance by reducing duplicated reflect usage on the same variable across packages.
func (r *Request) Parse(pointer interface{}) error {
var (
@ -52,15 +56,20 @@ func (r *Request) Parse(pointer interface{}) error {
reflectKind2 = reflectVal2.Kind()
)
switch reflectKind2 {
// Single struct, post content like:
// {"id":1, "name":"john"}
case reflect.Ptr, reflect.Struct:
// Struct conversion.
// Conversion.
if err := r.GetStruct(pointer); err != nil {
return err
}
// Struct validation.
// Validation.
if err := gvalid.CheckStruct(pointer, nil); err != nil {
return err
}
// Multiple struct, post content like:
// [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
case reflect.Array, reflect.Slice:
// If struct slice conversion, it might post JSON/XML content,
// so it uses gjson for the conversion.

View File

@ -13,27 +13,29 @@ import (
"github.com/gogf/gf/os/gtime"
)
// COOKIE对象非并发安全。
// Cookie for HTTP COOKIE management.
type Cookie struct {
data map[string]CookieItem // 数据项
path string // 默认的cookie path
domain string // 默认的cookie domain
maxage time.Duration // 默认的cookie maxage
server *Server // 所属Server
request *Request // 所属HTTP请求对象
response *Response // 所属HTTP返回对象
data map[string]CookieItem // Underlying cookie items.
path string // The default cookie path.
domain string // The default cookie domain
maxage time.Duration // The default cookie maxage.
server *Server // Belonged HTTP server
request *Request // Belonged HTTP request.
response *Response // Belonged HTTP response.
}
// cookie项
// CookieItem is cookie item stored in Cookie management object.
type CookieItem struct {
value string
domain string // 有效域名
path string // 有效路径
expireAt int64 // 过期时间
value string // Cookie value.
domain string // Cookie domain.
path string // Cookie path.
expireAt int64 // Cookie expiration timestamp.
httpOnly bool
}
// 获取或者创建一个COOKIE对象与传入的请求对应(延迟初始化)
// GetCookie creates or retrieves a cookie object with given request.
// It retrieves and returns an existing cookie object if it already exists with given request.
// It creates and returns a new cookie object if it does not exist with given request.
func GetCookie(r *Request) *Cookie {
if r.Cookie != nil {
return r.Cookie
@ -44,7 +46,7 @@ func GetCookie(r *Request) *Cookie {
}
}
// 从请求流中初始化,无锁,延迟初始化
// init does lazy initialization for cookie object.
func (c *Cookie) init() {
if c.data == nil {
c.data = make(map[string]CookieItem)
@ -64,7 +66,7 @@ func (c *Cookie) init() {
}
}
// 获取所有的Cookie并构造成map[string]string返回.
// Map returns the cookie items as map[string]string.
func (c *Cookie) Map() map[string]string {
c.init()
m := make(map[string]string)
@ -74,7 +76,7 @@ func (c *Cookie) Map() map[string]string {
return m
}
// 判断Cookie中是否存在制定键名(并且没有过期)
// Contains checks if given key exists and not expired in cookie.
func (c *Cookie) Contains(key string) bool {
c.init()
if r, ok := c.data[key]; ok {
@ -85,12 +87,14 @@ func (c *Cookie) Contains(key string) bool {
return false
}
// 设置cookie使用默认参数
// Set sets cookie item with default domain, path and expiration age.
func (c *Cookie) Set(key, value string) {
c.SetCookie(key, value, c.domain, c.path, c.server.GetCookieMaxAge())
}
// 设置cookie带详细cookie参数
// SetCookie sets cookie item given given domain, path and expiration age.
// The optional parameter <httpOnly> specifies if the cookie item is only available in HTTP,
// which is usually empty.
func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, httpOnly ...bool) {
c.init()
isHttpOnly := false
@ -102,17 +106,18 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration
}
}
// 获得客户端提交的SessionId
// GetSessionId retrieves and returns the session id from cookie.
func (c *Cookie) GetSessionId() string {
return c.Get(c.server.GetSessionIdName())
}
// 设置SessionId到Cookie
// SetSessionId sets session id in the cookie.
func (c *Cookie) SetSessionId(id string) {
c.Set(c.server.GetSessionIdName(), id)
}
// 查询cookie
// Get retrieves and returns the value with specified key.
// It returns <def> if specified key does not exist and <def> is given.
func (c *Cookie) Get(key string, def ...string) string {
c.init()
if r, ok := c.data[key]; ok {
@ -126,24 +131,26 @@ func (c *Cookie) Get(key string, def ...string) string {
return ""
}
// 删除COOKIE使用默认的domain&path
// Remove deletes specified key and its value from cookie using default domain and path.
// It actually tells the http client that the cookie is expired, do not send it to server next time.
func (c *Cookie) Remove(key string) {
c.SetCookie(key, "", c.domain, c.path, -86400)
}
// 标记该cookie在对应的域名和路径失效
// 删除cookie的重点是需要通知浏览器客户端cookie已过期
// RemoveCookie deletes specified key and its value from cookie using given domain and path.
// It actually tells the http client that the cookie is expired, do not send it to server next time.
func (c *Cookie) RemoveCookie(key, domain, path string) {
c.SetCookie(key, "", domain, path, -86400)
}
// 输出到客户端
// Output outputs the cookie items to client.
func (c *Cookie) Output() {
if len(c.data) == 0 {
return
}
for k, v := range c.data {
// 只有 expire != 0 的才是服务端在本次请求中设置的cookie
// Cookie item matches expire != 0 means it is set in this request,
// which should be outputted to client.
if v.expireAt == 0 {
continue
}

View File

@ -10,13 +10,13 @@ import (
"strings"
)
// 域名管理器对象
// Domain is used for route register for domains.
type Domain struct {
server *Server // 所属Server
domains map[string]struct{} // 多域名
server *Server // Belonged server
domains map[string]struct{} // Support multiple domains.
}
// 生成一个域名对象, 参数 domains 支持给定多个域名。
// Domain creates and returns a domain object for management for one or more domains.
func (s *Server) Domain(domains string) *Domain {
d := &Domain{
server: s,

View File

@ -19,19 +19,20 @@ import (
"os"
)
// 优雅的Web Server对象封装
// gracefulServer wraps the net/http.Server with graceful reload/restart feature.
type gracefulServer struct {
server *Server // Belonged server.
fd uintptr // 热重启时传递的socket监听文件句柄
address string // 监听地址信息
httpServer *http.Server // 底层http.Server
rawListener net.Listener // 原始listener
listener net.Listener // 接口化封装的listener
isHttps bool // 是否HTTPS
status int // 当前Server状态(关闭/运行)
fd uintptr // File descriptor for passing to child process when graceful reload.
address string // Listening address like:":80", ":8080".
httpServer *http.Server // Underlying http.Server.
rawListener net.Listener // Underlying net.Listener.
listener net.Listener // Wrapped net.Listener.
isHttps bool // Is HTTPS.
status int // Status of current server.
}
// 创建一个优雅的Http Server
// newGracefulServer creates and returns a graceful http server with given address.
// The optional parameter <fd> specifies the file descriptor which is passed from parent server.
func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
// Change port to address like: 80 -> :80
if gstr.IsNumeric(address) {
@ -42,14 +43,13 @@ func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
address: address,
httpServer: s.newHttpServer(address),
}
// 是否有继承的文件描述符
if len(fd) > 0 && fd[0] > 0 {
gs.fd = uintptr(fd[0])
}
return gs
}
// 生成一个底层的Web Server对象
// newGracefulServer creates and returns a underlying http.Server with given address.
func (s *Server) newHttpServer(address string) *http.Server {
server := &http.Server{
Addr: address,
@ -64,7 +64,7 @@ func (s *Server) newHttpServer(address string) *http.Server {
return server
}
// 执行HTTP监听
// ListenAndServe starts listening on configured address.
func (s *gracefulServer) ListenAndServe() error {
ln, err := s.getNetListener()
if err != nil {
@ -75,7 +75,8 @@ func (s *gracefulServer) ListenAndServe() error {
return s.doServe()
}
// 获得文件描述符
// Fd retrieves and returns the file descriptor of current server.
// It is available ony in *nix like operation systems like: linux, unix, darwin.
func (s *gracefulServer) Fd() uintptr {
if s.rawListener != nil {
file, err := s.rawListener.(*net.TCPListener).File()
@ -86,12 +87,14 @@ func (s *gracefulServer) Fd() uintptr {
return 0
}
// 设置自定义fd
// setFd sets the file descriptor for current server.
func (s *gracefulServer) setFd(fd int) {
s.fd = uintptr(fd)
}
// 执行HTTPS监听
// ListenAndServeTLS starts listening on configured address with HTTPS.
// The parameter <certFile> and <keyFile> specify the necessary certification and key files for HTTPS.
// The optional parameter <tlsConfig> specifies the custom TLS configuration.
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error {
var config *tls.Config
if len(tlsConfig) > 0 && tlsConfig[0] != nil {
@ -122,7 +125,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig .
return s.doServe()
}
// 获取服务协议字符串
// getProto retrieves and returns the proto string of current server.
func (s *gracefulServer) getProto() string {
proto := "http"
if s.isHttps {
@ -131,7 +134,7 @@ func (s *gracefulServer) getProto() string {
return proto
}
// 开始执行Web Server服务处理
// doServe does staring the serving.
func (s *gracefulServer) doServe() error {
action := "started"
if s.fd != 0 {
@ -147,7 +150,7 @@ func (s *gracefulServer) doServe() error {
return err
}
// 自定义的net.Listener
// getNetListener retrieves and returns the wrapped net.Listener.
func (s *gracefulServer) getNetListener() (net.Listener, error) {
var ln net.Listener
var err error
@ -167,7 +170,7 @@ func (s *gracefulServer) getNetListener() (net.Listener, error) {
return ln, err
}
// 执行请求优雅关闭
// shutdown shuts down the server gracefully.
func (s *gracefulServer) shutdown() {
if s.status == SERVER_STATUS_STOPPED {
return
@ -180,7 +183,7 @@ func (s *gracefulServer) shutdown() {
}
}
// 执行请求强制关闭
// close shuts down the server forcibly.
func (s *gracefulServer) close() {
if s.status == SERVER_STATUS_STOPPED {
return

View File

@ -24,38 +24,42 @@ import (
"github.com/gogf/gf/os/gtime"
)
// 默认HTTP Server处理入口http包底层默认使用了gorutine异步处理请求所以这里不再异步执行
// ServeHTTP is the default handler for http request.
// It should not create new goroutine handling the request as
// it's called by am already created new goroutine from http.Server.
//
// This function also make serve implementing the interface of http.Handler.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Max body size limit.
if s.config.ClientMaxBodySize > 0 {
r.Body = http.MaxBytesReader(w, r.Body, s.config.ClientMaxBodySize)
}
// 重写规则判断
// Rewrite feature checks.
if len(s.config.Rewrites) > 0 {
if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok {
r.URL.Path = rewrite
}
}
// 去掉末尾的"/"号
// Remove char '/' in the tail of URI.
if r.URL.Path != "/" {
for len(r.URL.Path) > 0 && r.URL.Path[len(r.URL.Path)-1] == '/' {
r.URL.Path = r.URL.Path[:len(r.URL.Path)-1]
}
}
// URI默认值
// Default URI value if it's empty.
if r.URL.Path == "" {
r.URL.Path = "/"
}
// 创建请求处理对象
// Create a new request object.
request := newRequest(s, r, w)
defer func() {
// 设置请求完成时间
request.LeaveTime = gtime.TimestampMilli()
// error log
// error log handling.
if request.error != nil {
s.handleErrorLog(request.error, request)
} else {
@ -64,18 +68,20 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.handleErrorLog(gerror.Newf("%v", exception), request)
}
}
// access log
// access log handling.
s.handleAccessLog(request)
// 关闭当前Session并更新会话超时时间
// Close the session, which automatically update the TTL
// of the session if it exists.
request.Session.Close()
}()
// ============================================================
// 优先级控制:
// 静态文件 > 动态服务 > 静态目录
// Priority:
// Static File > Dynamic Service > Static Directory
// ============================================================
// 优先执行静态文件检索(检测是否存在对应的静态文件包括index files处理)
// Search the static file with most high priority,
// which also handle the index files feature.
if s.config.FileServerEnabled {
request.StaticFile = s.searchStaticFile(r.URL.Path)
if request.StaticFile != nil {
@ -83,29 +89,29 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// 动态服务检索
// Search the dynamic service handler.
request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)
// 判断最终对该请求提供的服务方式
// Check the service type static or dynamic for current request.
if request.StaticFile != nil && request.StaticFile.IsDir && request.hasServeHandler {
request.isFileRequest = false
}
// 事件 - BeforeServe
// HOOK - BeforeServe
s.callHookHandler(HOOK_BEFORE_SERVE, request)
// 执行静态文件服务/回调控制器/执行对象/方法
// Core serving handling.
if !request.IsExited() {
if request.isFileRequest {
// 静态服务
// Static file service.
s.serveFile(request, request.StaticFile)
} else {
if len(request.handlers) > 0 {
// 动态服务
// Dynamic service.
request.Middleware.Next()
} else {
if request.StaticFile != nil && request.StaticFile.IsDir {
// 静态目录
// Serve the directory.
s.serveFile(request, request.StaticFile)
} else {
if len(request.Response.Header()) == 0 &&
@ -118,12 +124,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// 事件 - AfterServe
// HOOK - AfterServe
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_SERVE, request)
}
// 事件 - BeforeOutput
// HOOK - BeforeOutput
if !request.IsExited() {
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
}
@ -146,15 +152,16 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// 设置Session Id到Cookie
// Automatically set the session id to cookie
// if it creates a new session id in this request.
if request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() {
request.Cookie.SetSessionId(request.Session.Id())
}
// 输出Cookie
// Output the cookie content to client.
request.Cookie.Output()
// 输出缓冲区
// Output the buffer content to client.
request.Response.Output()
// 事件 - AfterOutput
// HOOK - AfterOutput
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
@ -222,9 +229,10 @@ func (s *Server) searchStaticFile(uri string) *StaticFile {
return nil
}
// http server静态文件处理path可以为相对路径也可以为绝对路径
// serveFile serves the static file for client.
// The optional parameter <allowIndex> specifies if allowing directory listing if <f> is directory.
func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
// 使用资源文件
// Use resource file from memory.
if f.File != nil {
if f.IsDir {
if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
@ -239,7 +247,7 @@ func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
}
return
}
// 使用磁盘文件
// Use file from dist.
file, err := os.Open(f.Path)
if err != nil {
r.Response.WriteStatus(http.StatusForbidden)
@ -264,7 +272,7 @@ func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
}
}
// 显示目录列表
// listDir lists the sub files of specified directory as HTML content to client.
func (s *Server) listDir(r *Request, f http.File) {
files, err := f.Readdir(-1)
if err != nil {

View File

@ -17,7 +17,7 @@ func (s *Server) Logger() *glog.Logger {
return s.config.Logger
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
// handleAccessLog handles the access logging for server.
func (s *Server) handleAccessLog(r *Request) {
if !s.IsAccessLogEnabled() {
return
@ -37,14 +37,13 @@ func (s *Server) handleAccessLog(r *Request) {
)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
// handleErrorLog handles the error logging for server.
func (s *Server) handleErrorLog(err error, r *Request) {
// 错误输出默认是开启的
// It does nothing if error logging is custom disabled.
if !s.IsErrorLogEnabled() {
return
}
// 错误日志信息
scheme := "http"
if r.TLS != nil {
scheme = "https"

View File

@ -11,7 +11,7 @@ import (
"net/http"
)
// 绑定指定的hook回调函数, pattern参数同BindHandler支持命名路由hook参数的值由ghttp server设定参数不区分大小写
// BindHookHandler registers handler for specified hook.
func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) {
s.doBindHookHandler(pattern, hook, handler, "")
}
@ -26,23 +26,22 @@ func (s *Server) doBindHookHandler(pattern string, hook string, handler HandlerF
})
}
// 通过map批量绑定回调函数
func (s *Server) BindHookHandlerByMap(pattern string, hookMap map[string]HandlerFunc) {
for k, v := range hookMap {
s.BindHookHandler(pattern, k, v)
}
}
// 事件回调处理,内部使用了缓存处理.
// 并按照指定hook回调函数的优先级及注册顺序进行调用
// callHookHandler calls the hook handler by their registered sequences.
func (s *Server) callHookHandler(hook string, r *Request) {
hookItems := r.getHookHandlers(hook)
if len(hookItems) > 0 {
// 备份原有的router变量
// Backup the old router variable map.
oldRouterMap := r.routerMap
for _, item := range hookItems {
r.routerMap = item.values
// 不使用hook的router对象保留路由注册服务的router对象不能覆盖
// DO NOT USE the router of the hook handler,
// which can overwrite the router of serving handler.
// r.Router = item.handler.router
if err := s.niceCallHookHandler(item.handler.itemFunc, r); err != nil {
switch err {
@ -58,12 +57,12 @@ func (s *Server) callHookHandler(hook string, r *Request) {
}
}
}
// 恢复原有的router变量
// Restore the old router variable map.
r.routerMap = oldRouterMap
}
}
// 获得当前请求,指定类型的的钩子函数列表
// getHookHandlers retrieves and returns the hook handlers of specified hook.
func (r *Request) getHookHandlers(hook string) []*handlerParsedItem {
if !r.hasHookHandler {
return nil
@ -79,7 +78,9 @@ func (r *Request) getHookHandlers(hook string) []*handlerParsedItem {
return parsedItems
}
// 友好地调用方法
// niceCallHookHandler nicely calls the hook handler function,
// which means it automatically catches and returns the possible panic error to
// avoid goroutine crash.
func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{}) {
defer func() {
err = recover()

View File

@ -8,4 +8,5 @@ package ghttp
import "github.com/gogf/gf/os/gsession"
// Session is actually a alias of gsession.Session.
type Session = gsession.Session

View File

@ -8,6 +8,8 @@ package ghttp
import "github.com/gorilla/websocket"
// WebSocket wraps the underlying websocket connection
// and provides convenient functions.
type WebSocket struct {
*websocket.Conn
}

View File

@ -431,7 +431,7 @@ func (c *memCache) syncEventAndClearExpired() {
}
// clearByKey deletes the key-value pair with given <key>.
// The parameter <force> specifies whether doing this deleting forcedly.
// The parameter <force> specifies whether doing this deleting forcibly.
func (c *memCache) clearByKey(key interface{}, force ...bool) {
c.dataMu.Lock()
// Doubly check before really deleting it from cache.