diff --git a/g/net/ghttp/http.go b/g/net/ghttp/http.go index a5180bd6c..0d32729f7 100644 --- a/g/net/ghttp/http.go +++ b/g/net/ghttp/http.go @@ -1,107 +1,2 @@ package ghttp -import ( - "net/http" - "time" - "crypto/tls" - "log" - "net/url" -) - -// http客户端 -type Client struct { - http.Client -} - -// http server结构体 -type Server struct { - server http.Server - config ServerConfig - handlerMap HandlerMap -} - -// 请求对象 -type ClientRequest struct { - http.Request - getvals *url.Values -} - -// 客户端请求结果对象 -type ClientResponse struct { - http.Response -} - -// 服务端请求返回对象 -type ServerResponse struct { - http.ResponseWriter -} - -// http回调函数 -type HandlerFunc func(*ClientRequest, *ServerResponse) - -// uri与回调函数的绑定记录表 -type HandlerMap map[string]HandlerFunc - -// HTTP Server 设置结构体 -type ServerConfig struct { - // HTTP Server基础字段 - Addr string // 监听IP和端口,监听本地所有IP使用":端口" - Handler http.Handler // 默认的处理函数 - TLSConfig *tls.Config // TLS配置 - ReadTimeout time.Duration - WriteTimeout time.Duration - IdleTimeout time.Duration - MaxHeaderBytes int // 最大的header长度 - ErrorLog *log.Logger // 错误日志的处理接口 - // gf 扩展信息字段 - IndexFiles []string // 默认访问的文件列表 - IndexFolder bool // 如果访问目录是否显示目录列表 - ServerAgent string // server agent - ServerRoot string // 服务器服务的本地目录根路径 -} - -// 默认HTTP Server -var defaultServerConfig = ServerConfig { - Addr : ":80", - 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 : "", -} - -// 修改默认的http server配置 -func SetDefaultServerConfig (c ServerConfig) { - defaultServerConfig = c -} - -// 创建一个默认配置的HTTP Server(默认监听端口是80) -func NewServer() (*Server) { - return NewServerByConfig(defaultServerConfig) -} - -// 创建一个HTTP Server,返回指针 -func NewServerByAddr(addr string) (*Server) { - config := defaultServerConfig - config.Addr = addr - return NewServerByConfig(config) -} - -// 创建一个HTTP Server -func NewServerByAddrRoot(addr string, root string) (*Server) { - config := defaultServerConfig - config.Addr = addr - config.ServerRoot = root - return NewServerByConfig(config) -} - -// 根据输入配置创建一个http server对象 -func NewServerByConfig(s ServerConfig) (*Server) { - var server Server - server.SetConfig(s) - return &server -} \ No newline at end of file diff --git a/g/net/ghttp/http_client.go b/g/net/ghttp/http_client.go index 0f1877591..0fd08e395 100644 --- a/g/net/ghttp/http_client.go +++ b/g/net/ghttp/http_client.go @@ -4,8 +4,26 @@ import ( "net/http" "strings" "time" + "net/url" + "bytes" ) +// http客户端 +type Client struct { + http.Client +} + +// 请求对象 +type ClientRequest struct { + http.Request + getvals *url.Values // GET参数 +} + +// 客户端请求结果对象 +type ClientResponse struct { + http.Response +} + // http客户端对象指针 func NewClient() (*Client) { return &Client{} @@ -17,106 +35,104 @@ func (c *Client) SetTimeOut(t time.Duration) { } // GET请求 -func (c *Client) Get(url string) *ClientResponse { - return c.Request("GET", url, "") +func (c *Client) Get(url string) (*ClientResponse, error) { + return c.Request("GET", url, []byte("")) } // PUT请求 -func (c *Client) Put(url, data string) *ClientResponse { - return c.Request("PUT", url, data) +func (c *Client) Put(url, data string) (*ClientResponse, error) { + return c.Request("PUT", url, []byte(data)) } // POST请求提交数据 -func (c *Client) Post(url, data string) *ClientResponse { +func (c *Client) Post(url, data string) (*ClientResponse, error) { resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data)) if err != nil { - //glog.Println(err) - return nil + return nil, err } r := &ClientResponse{} r.Response = *resp - return r + return r, nil } // DELETE请求 -func (c *Client) Delete(url, data string) *ClientResponse { - return c.Request("DELETE", url, data) +func (c *Client) Delete(url, data string) (*ClientResponse, error) { + return c.Request("DELETE", url, []byte(data)) } -func (c *Client) Head(url, data string) *ClientResponse { - return c.Request("HEAD", url, data) +func (c *Client) Head(url, data string) (*ClientResponse, error) { + return c.Request("HEAD", url, []byte(data)) } -func (c *Client) Patch(url, data string) *ClientResponse { - return c.Request("PATCH", url, data) +func (c *Client) Patch(url, data string) (*ClientResponse, error) { + return c.Request("PATCH", url, []byte(data)) } -func (c *Client) Connect(url, data string) *ClientResponse{ - return c.Request("CONNECT", url, data) +func (c *Client) Connect(url, data string) (*ClientResponse, error) { + return c.Request("CONNECT", url, []byte(data)) } -func (c *Client) Options(url, data string) *ClientResponse{ - return c.Request("OPTIONS", url, data) +func (c *Client) Options(url, data string) (*ClientResponse, error) { + return c.Request("OPTIONS", url, []byte(data)) } -func (c *Client) Trace(url, data string) *ClientResponse { - return c.Request("TRACE", url, data) +func (c *Client) Trace(url, data string) (*ClientResponse, error) { + return c.Request("TRACE", url, []byte(data)) } -// 请求并返回response对象 -func (c *Client) Request(method, url, data string) *ClientResponse { - req, err := http.NewRequest(strings.ToUpper(method), url, strings.NewReader(data)) +// 请求并返回response对象,该方法支持二进制提交数据 +func (c *Client) Request(method, url string, data []byte) (*ClientResponse, error) { + req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(data)) if err != nil { - //glog.Println("creating request failed: " + err.Error()) - return nil + return nil, err } resp, err := c.Do(req) if err != nil { - //glog.Println("sending request failed: " + err.Error()) - return nil + return nil, err } r := &ClientResponse{} r.Response = *resp - return r + return r, nil } -func Get(url string) *ClientResponse { - return Request("GET", url, "") +func Get(url string) (*ClientResponse, error) { + return Request("GET", url, []byte("")) } -func Put(url, data string) *ClientResponse { - return Request("PUT", url, data) +func Put(url, data string) (*ClientResponse, error) { + return Request("PUT", url, []byte(data)) } -func Post(url, data string) *ClientResponse { - return Request("PUT", url, data) +func Post(url, data string) (*ClientResponse, error) { + return Request("PUT", url, []byte(data)) } -func Delete(url, data string) *ClientResponse { - return Request("DELETE", url, data) +func Delete(url, data string) (*ClientResponse, error) { + return Request("DELETE", url, []byte(data)) } -func Head(url, data string) *ClientResponse { - return Request("HEAD", url, data) +func Head(url, data string) (*ClientResponse, error) { + return Request("HEAD", url, []byte(data)) } -func Patch(url, data string) *ClientResponse { - return Request("PATCH", url, data) +func Patch(url, data string) (*ClientResponse, error) { + return Request("PATCH", url, []byte(data)) } -func Connect(url, data string) *ClientResponse{ - return Request("CONNECT", url, data) +func Connect(url, data string) (*ClientResponse, error) { + return Request("CONNECT", url, []byte(data)) } -func Options(url, data string) *ClientResponse{ - return Request("OPTIONS", url, data) +func Options(url, data string) (*ClientResponse, error) { + return Request("OPTIONS", url, []byte(data)) } -func Trace(url, data string) *ClientResponse { - return Request("TRACE", url, data) +func Trace(url, data string) (*ClientResponse, error) { + return Request("TRACE", url, []byte(data)) } -func Request(method, url, data string) *ClientResponse { +// 该方法支持二进制提交数据 +func Request(method, url string, data []byte) (*ClientResponse, error) { return NewClient().Request(method, url, data) } diff --git a/g/net/ghttp/http_request.go b/g/net/ghttp/http_client_request.go similarity index 100% rename from g/net/ghttp/http_request.go rename to g/net/ghttp/http_client_request.go diff --git a/g/net/ghttp/http_client_response.go b/g/net/ghttp/http_client_response.go new file mode 100644 index 000000000..4c615ad36 --- /dev/null +++ b/g/net/ghttp/http_client_response.go @@ -0,0 +1,20 @@ +package ghttp + +import ( + "io/ioutil" +) + +// 获取返回的数据 +func (r *ClientResponse) ReadAll() []byte { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil + } + return body +} + +// 关闭返回的HTTP链接 +func (r *ClientResponse) Close() { + r.Response.Close = true + r.Body.Close() +} \ No newline at end of file diff --git a/g/net/ghttp/http_controller.go b/g/net/ghttp/http_controller.go index 1b65b2181..97f33ea96 100644 --- a/g/net/ghttp/http_controller.go +++ b/g/net/ghttp/http_controller.go @@ -5,25 +5,12 @@ type Controller struct { Server *Server } -// 控制器接口 -type ControllerApi interface { - Get(r *ClientRequest, w *ServerResponse) - Put(r *ClientRequest, w *ServerResponse) - Post(r *ClientRequest, w *ServerResponse) - Delete(r *ClientRequest, w *ServerResponse) - Head(r *ClientRequest, w *ServerResponse) - Patch(r *ClientRequest, w *ServerResponse) - Connect(r *ClientRequest, w *ServerResponse) - Options(r *ClientRequest, w *ServerResponse) - Trace(r *ClientRequest, w *ServerResponse) -} - -func (c *Controller) Get(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Put(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Post(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Delete(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Head(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Patch(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Connect(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Options(r *ClientRequest, w *ServerResponse) {} -func (c *Controller) Trace(r *ClientRequest, w *ServerResponse) {} \ No newline at end of file +func (c *Controller) Get(*ClientRequest, *ServerResponse) {} +func (c *Controller) Put(*ClientRequest, *ServerResponse) {} +func (c *Controller) Post(*ClientRequest, *ServerResponse) {} +func (c *Controller) Delete(*ClientRequest, *ServerResponse) {} +func (c *Controller) Head(*ClientRequest, *ServerResponse) {} +func (c *Controller) Patch(*ClientRequest, *ServerResponse) {} +func (c *Controller) Connect(*ClientRequest, *ServerResponse) {} +func (c *Controller) Options(*ClientRequest, *ServerResponse) {} +func (c *Controller) Trace(*ClientRequest, *ServerResponse) {} \ No newline at end of file diff --git a/g/net/ghttp/http_controller_rest.go b/g/net/ghttp/http_controller_rest.go new file mode 100644 index 000000000..183ab6a17 --- /dev/null +++ b/g/net/ghttp/http_controller_rest.go @@ -0,0 +1,14 @@ +package ghttp + +// RESTful控制器接口 +type ControllerRest interface { + Get(*ClientRequest, *ServerResponse) + Put(*ClientRequest, *ServerResponse) + Post(*ClientRequest, *ServerResponse) + Delete(*ClientRequest, *ServerResponse) + Head(*ClientRequest, *ServerResponse) + Patch(*ClientRequest, *ServerResponse) + Connect(*ClientRequest, *ServerResponse) + Options(*ClientRequest, *ServerResponse) + Trace(*ClientRequest, *ServerResponse) +} \ No newline at end of file diff --git a/g/net/ghttp/http_response.go b/g/net/ghttp/http_response.go deleted file mode 100644 index 0a1fac0ed..000000000 --- a/g/net/ghttp/http_response.go +++ /dev/null @@ -1,35 +0,0 @@ -package ghttp - -import ( - "gitee.com/johng/gf/g/encoding/gjson" - "io/ioutil" - "gitee.com/johng/gf/g/os/glog" -) - -type ResponseJson struct { - Result int `json:"result"` - Message string `json:"message"` - Data interface{} `json:"data"` -} - -// 关闭返回的HTTP链接 -func (r *ClientResponse) Close() { - r.Response.Close = true - r.Body.Close() -} - -// 返回固定格式的json -func (r *ServerResponse) ResponseJson(result int, message string, data interface{}) { - r.Header().Set("Content-type", "application/json") - r.Write([]byte(gjson.Encode(ResponseJson{ result, message, data }))) -} - -// 获取返回的数据 -func (r *ClientResponse) ReadAll() string { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - glog.Println(err) - return "" - } - return string(body) -} \ No newline at end of file diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index ae1cb0a96..1cfe5c912 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -7,16 +7,38 @@ import ( "crypto/tls" "time" "log" - "regexp" - "gitee.com/johng/gf/g/os/glog" + "sync" + "errors" ) +// http server结构体 +type Server struct { + hmu sync.RWMutex // handlerMap互斥锁 + server http.Server // 底层http server对象 + config ServerConfig // 配置对象 + handlerMap HandlerMap // 回调函数 +} + +// http回调函数 +type HandlerFunc func(*ClientRequest, *ServerResponse) + +// uri与回调函数的绑定记录表 +type HandlerMap map[string]HandlerFunc + +// 创建一个默认配置的HTTP Server(默认监听端口是80) +func NewServer() (*Server) { + server := Server{} + server.SetConfig(defaultServerConfig) + return &server +} + // 执行 func (s *Server)Run() error { // 底层http server配置 if s.config.Handler == nil { s.config.Handler = http.HandlerFunc(s.defaultHttpHandle) } + // 底层http server初始化 s.server = http.Server { Addr : s.config.Addr, Handler : s.config.Handler, @@ -26,16 +48,10 @@ func (s *Server)Run() error { MaxHeaderBytes : s.config.MaxHeaderBytes, } // 执行端口监听 - err := s.server.ListenAndServe() - if err != nil { - glog.Fatalln(err) + if err := s.server.ListenAndServe(); err != nil { + return err } - return err -} - -// 获取默认的http server设置 -func (h Server)GetDefaultSetting() ServerConfig { - return defaultServerConfig + return nil } // http server setting设置 @@ -121,23 +137,25 @@ func (s *Server)SetServerRoot(root string) { // 绑定URI到操作函数/方法 // pattern的格式形如:/user/list, put:/user, delete:/user // 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server)BindHandle(pattern string, handler HandlerFunc ) { +func (s *Server)BindHandle(pattern string, handler HandlerFunc) error { + s.hmu.Lock() + defer s.hmu.Unlock() if s.handlerMap == nil { s.handlerMap = make(HandlerMap) } key := "" - reg := regexp.MustCompile(`(\w+?)\s*:\s*(.+)`) - result := reg.FindStringSubmatch(pattern) + result := strings.Split(pattern, ":") if len(result) > 1 { - key = strings.ToUpper(result[1]) + ":" + result[2] + key = strings.ToUpper(result[0]) + ":" + result[1] } else { key = strings.TrimSpace(pattern) } if _, ok := s.handlerMap[key]; ok { - panic("duplicated http server handler for: " + pattern) + return errors.New("duplicated http server handler for: " + pattern) } else { s.handlerMap[key] = handler } + return nil } // 通过映射数组绑定URI到操作函数/方法 @@ -148,7 +166,7 @@ func (s *Server)BindHandleByMap(m HandlerMap) { } // 绑定控制器,控制器需要继承gmvc.ControllerBase对象并实现需要的REST方法 -func (s *Server)BindController(uri string, c ControllerApi) { +func (s *Server)BindController(uri string, c ControllerRest) { s.BindHandleByMap(HandlerMap{ "GET:" + uri : c.Get, "PUT:" + uri : c.Put, diff --git a/g/net/ghttp/http_server_config.go b/g/net/ghttp/http_server_config.go new file mode 100644 index 000000000..0860d444f --- /dev/null +++ b/g/net/ghttp/http_server_config.go @@ -0,0 +1,46 @@ +package ghttp + +import ( + "net/http" + "crypto/tls" + "time" + "log" + "gitee.com/johng/gf/g/os/gfile" +) + +// HTTP Server 设置结构体 +type ServerConfig struct { + // HTTP Server基础字段 + Addr string // 监听IP和端口,监听本地所有IP使用":端口" + Handler http.Handler // 默认的处理函数 + TLSConfig *tls.Config // TLS配置 + ReadTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration + MaxHeaderBytes int // 最大的header长度 + ErrorLog *log.Logger // 错误日志的处理接口 + // gf 扩展信息字段 + IndexFiles []string // 默认访问的文件列表 + IndexFolder bool // 如果访问目录是否显示目录列表 + ServerAgent string // server agent + ServerRoot string // 服务器服务的本地目录根路径 +} + +// 默认HTTP Server +var defaultServerConfig = ServerConfig { + Addr : ":80", + 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 : gfile.SelfDir(), +} + +// 获取默认的http server设置 +func DefaultSetting() ServerConfig { + return defaultServerConfig +} \ No newline at end of file diff --git a/g/net/ghttp/http_server_handle.go b/g/net/ghttp/http_server_handle.go index 56c04f520..df6ab9b47 100644 --- a/g/net/ghttp/http_server_handle.go +++ b/g/net/ghttp/http_server_handle.go @@ -15,8 +15,8 @@ import ( // 默认HTTP Server处理入口,底层默认使用了gorutine调用该接口 func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) { request := ClientRequest{} - response := ServerResponse {} - request.Request = *r + response := ServerResponse {server : s} + request.Request = *r response.ResponseWriter = w if f, ok := s.handlerMap[r.URL.Path]; ok { f(&request, &response) @@ -93,8 +93,7 @@ func (s *Server)listDir(w http.ResponseWriter, f http.File) { if d.IsDir() { name += "/" } - url := url.URL{Path: name} - fmt.Fprintf(w, "%s\n", url.String(), ghtml.SpecialChars(name)) + fmt.Fprintf(w, "%s\n", url.URL{Path: name}.String(), ghtml.SpecialChars(name)) } fmt.Fprintf(w, "\n") } diff --git a/g/net/ghttp/http_server_response.go b/g/net/ghttp/http_server_response.go new file mode 100644 index 000000000..80561dd4e --- /dev/null +++ b/g/net/ghttp/http_server_response.go @@ -0,0 +1,36 @@ +package ghttp + +import ( + "net/http" + "gitee.com/johng/gf/g/encoding/gjson" +) + +// 服务端请求返回对象 +type ServerResponse struct { + http.ResponseWriter + server *Server // 所属Server对象 +} + +// 返回的固定JSON数据结构 +type ResponseJson struct { + Result int `json:"result"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + +// 返回信息 +func (r *ServerResponse) Response(content []byte) { + if r.Header().Get("Content-Type") == "" { + r.Header().Set("Content-Type", "text/plain; charset=utf-8") + } + r.Write(content) +} + +// 返回固定格式的json +func (r *ServerResponse) ResponseJson(result int, message string, data interface{}) { + if r.Header().Get("Content-Type") == "" { + r.Header().Set("Content-Type", "application/json") + } + r.Write([]byte(gjson.Encode(ResponseJson{ result, message, data }))) +} + diff --git a/g/os/gcache/gcache.go b/g/os/gcache/gcache.go index 0f1c99b34..e02be91de 100644 --- a/g/os/gcache/gcache.go +++ b/g/os/gcache/gcache.go @@ -8,35 +8,41 @@ import ( ) const ( - gCACHE_GROUP_SIZE = 4 // 缓存分区大小,不能超过uint8的最大值 + gDEFAULT_CACHE_GROUP_SIZE = 4 // 默认缓存分区大小,不能超过uint8的最大值 ) +// 缓存对象 type Cache struct { sync.RWMutex - m map[uint8]*CacheMap // 以分区大小数字作为索引 + g uint8 // 分区大小 + m map[uint8]*CacheMap // 以分区大小数字作为索引 } +// 缓存分区对象 type CacheMap struct { sync.RWMutex - deleted bool // 对象是否已删除,以便判断停止goroutine + closed bool // 对象是否已删除,以便判断停止goroutine m map[string]CacheItem // 键值对 } +// 缓存数据项 type CacheItem struct { v interface{} // 缓存键值 e int64 // 过期时间 } // 全局缓存管理对象 -var cache *Cache = New() +var cache *Cache = New(gDEFAULT_CACHE_GROUP_SIZE) // Cache对象按照缓存键名首字母做了分组 -func New() *Cache { +func New(group uint8) *Cache { c := &Cache { + g : group, m : make(map[uint8]*CacheMap), } + // 初始化分区对象 var i uint8 = 0 - for ; i < gCACHE_GROUP_SIZE; i++ { + for ; i < group; i++ { m := &CacheMap { m : make(map[string]CacheItem), } @@ -158,20 +164,14 @@ func (c *Cache) Size() int { func (c *Cache) Close() { c.RLock() for _, cm := range c.m { - cm.Lock() - cm.deleted = true - cm.Unlock() + cm.Close() } c.RUnlock() - - c.Lock() - c.m = nil - c.Unlock() } // 计算缓存的索引 func (c *Cache) getIndex(k string) uint8 { - return uint8(ghash.BKDRHash([]byte(k)) % gCACHE_GROUP_SIZE) + return uint8(ghash.BKDRHash([]byte(k)) % uint32(c.g)) } // 设置kv缓存键值对,过期时间单位为毫秒 @@ -207,26 +207,32 @@ func (cm *CacheMap) Remove(k string) { cm.Unlock() } -// 自动清理过期键值对(每间隔60秒执行) +// 关闭缓存分区 +func (cm *CacheMap) Close() { + cm.Lock() + cm.closed = true + cm.Unlock() +} + +// 是否删除 +func (cm *CacheMap) isClosed() bool { + cm.RLock() + r := cm.closed + cm.RUnlock() + return r +} + +// 自动清理过期键值对(每间隔3秒执行) func (cm *CacheMap) autoClearLoop() { - for !cm.deleted { - expired := make([]string, 0) - cm.RLock() + for !cm.isClosed() { + cm.Lock() for k, v := range cm.m { if v.e > 0 && v.e < gtime.Millisecond() { - expired = append(expired, k) - } - } - cm.RUnlock() - - if len(expired) > 0 { - cm.Lock() - for _, k := range expired { delete(cm.m, k) } - cm.Unlock() } - time.Sleep(60 * time.Second) + cm.Unlock() + time.Sleep(3 * time.Second) } }