mirror of
https://gitee.com/johng/gf
synced 2026-06-12 20:23:22 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93763192f2 | |||
| 80e0eae6b0 | |||
| 4e3d735b90 | |||
| 60e5a7da28 | |||
| 997b5ba889 | |||
| b3e7ca1963 | |||
| 5a82d695c1 | |||
| 64a0427150 |
@ -1,6 +1,7 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
|
||||
|
||||
52
README.MD
52
README.MD
@ -65,42 +65,30 @@ func main() {
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Contributors(TOP 10)
|
||||
# Contributors
|
||||
|
||||
<a href="https://gitee.com/johng" target="_blank" title="John"><img src="https://gitee.com/uploads/27/1309327_johng.png?1530630243" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://github.com/chenyang351" target="_blank" title="chenyang351"><img src="https://avatars1.githubusercontent.com/u/30063958?s=60&v=4" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/wxkj" target="_blank" title="wxkj"><img src="https://gitee.com/uploads/56/91356_wxkj.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://github.com/wxkj001" target="_blank" title="3wxkj001
|
||||
"><img src="https://avatars0.githubusercontent.com/u/7794279?s=60&v=4" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
<br /><br /><br />
|
||||
- [johng](https://gitee.com/johng)
|
||||
- [zhaopengme](https://github.com/zhaopengme)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [zseeker](https://gitee.com/zseeker)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [zhangjinfu](https://gitee.com/zhangjinfu)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
|
||||
# Donators
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/mg91" target="_blank" title="mg91"><img src="https://images.gitee.com/uploads/30/1410930_mg91.png" width="60" align="left"></a>
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
- [zhuhuan12](https://gitee.com/zhuhuan12)
|
||||
- [zfan_codes](https://gitee.com/zfan_codes)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
|
||||
|
||||
|
||||
|
||||
43
README_ZH.MD
43
README_ZH.MD
@ -85,30 +85,27 @@ func main() {
|
||||
<img src="https://goframe.org/images/donate.png" width="300"/>
|
||||
</a>
|
||||
|
||||
# 贡献者(TOP 10)
|
||||
# 贡献者
|
||||
|
||||
<a href="https://gitee.com/johng" target="_blank" title="John"><img src="https://gitee.com/uploads/27/1309327_johng.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/chenyang351" target="_blank" title="chenyang351"><img src="https://avatars1.githubusercontent.com/u/30063958?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wxkj" target="_blank" title="wxkj"><img src="https://gitee.com/uploads/56/91356_wxkj.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/wxkj001" target="_blank" title="3wxkj001
|
||||
"><img src="https://avatars0.githubusercontent.com/u/7794279?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
<br /><br /><br />
|
||||
- [johng](https://gitee.com/johng)
|
||||
- [zhaopengme](https://github.com/zhaopengme)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [zseeker](https://gitee.com/zseeker)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [zhangjinfu](https://gitee.com/zhangjinfu)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
|
||||
# 捐赠者
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/mg91" target="_blank" title="mg91"><img src="https://images.gitee.com/uploads/30/1410930_mg91.png" width="60" align="left"></a>
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
- [zhuhuan12](https://gitee.com/zhuhuan12)
|
||||
- [zfan_codes](https://gitee.com/zfan_codes)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
|
||||
@ -79,6 +79,11 @@ func NewUnsafe(value...interface{}) *Json {
|
||||
return New(nil, true)
|
||||
}
|
||||
|
||||
// 识别当前给定内容是否为JSON格式
|
||||
func Valid (v interface{}) bool {
|
||||
return json.Valid(gconv.Bytes(v))
|
||||
}
|
||||
|
||||
// 编码go变量为json字符串,并返回json字符串指针
|
||||
func Encode (v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"time"
|
||||
"bytes"
|
||||
@ -117,6 +118,7 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
|
||||
}
|
||||
req := (*http.Request)(nil)
|
||||
if strings.Contains(param, "@file:") {
|
||||
// 文件上传
|
||||
buffer := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(buffer)
|
||||
for _, item := range strings.Split(param, "&") {
|
||||
@ -150,11 +152,17 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
}
|
||||
} else {
|
||||
if r, err := http.NewRequest("POST", url, bytes.NewReader([]byte(param))); err != nil {
|
||||
// 识别提交数据格式
|
||||
paramBytes := []byte(param)
|
||||
if r, err := http.NewRequest("POST", url, bytes.NewReader(paramBytes)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
req = r
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
if json.Valid(paramBytes) {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 自定义header
|
||||
|
||||
@ -242,7 +242,7 @@ func (s *Server) Start() error {
|
||||
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
|
||||
}
|
||||
// 不允许访问的路由注册(使用HOOK实现)
|
||||
// @TODO 去掉HOOK的实现方式
|
||||
// TODO 去掉HOOK的实现方式
|
||||
if s.config.DenyRoutes != nil {
|
||||
for _, v := range s.config.DenyRoutes {
|
||||
s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) {
|
||||
@ -398,7 +398,8 @@ func Wait() {
|
||||
// 开启底层Web Server执行
|
||||
func (s *Server) startServer(fdMap listenerFdMap) {
|
||||
var httpsEnabled bool
|
||||
if len(s.config.HTTPSCertPath) > 0 && len(s.config.HTTPSKeyPath) > 0 {
|
||||
// 判断是否启用HTTPS
|
||||
if len(s.config.TLSConfig.Certificates) > 0 || (len(s.config.HTTPSCertPath) > 0 && len(s.config.HTTPSKeyPath) > 0) {
|
||||
// ================
|
||||
// HTTPS
|
||||
// ================
|
||||
@ -479,7 +480,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
|
||||
s.serverCount.Add(1)
|
||||
err := (error)(nil)
|
||||
if server.isHttps {
|
||||
err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath)
|
||||
err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath, &s.config.TLSConfig)
|
||||
} else {
|
||||
err = server.ListenAndServe()
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
@ -44,6 +45,7 @@ type ServerConfig struct {
|
||||
WriteTimeout time.Duration // 写入超时
|
||||
IdleTimeout time.Duration // 等待超时
|
||||
MaxHeaderBytes int // 最大的header长度
|
||||
TLSConfig tls.Config
|
||||
|
||||
// 静态文件配置
|
||||
IndexFiles []string // 默认访问的文件列表
|
||||
@ -191,28 +193,46 @@ func (s *Server)SetHTTPSPort(port...int) {
|
||||
}
|
||||
}
|
||||
|
||||
// 开启HTTPS支持,但是必须提供Cert和Key文件
|
||||
func (s *Server)EnableHTTPS(certFile, keyFile string) {
|
||||
// 开启HTTPS支持,但是必须提供Cert和Key文件,tlsConfig为可选项
|
||||
func (s *Server)EnableHTTPS(certFile, keyFile string, tlsConfig...tls.Config) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
|
||||
return
|
||||
}
|
||||
certFileRealPath := gfile.RealPath(certFile)
|
||||
if certFileRealPath == "" {
|
||||
certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFileRealPath)
|
||||
certFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + certFile)
|
||||
if certFileRealPath == "" {
|
||||
certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFile)
|
||||
}
|
||||
}
|
||||
if certFileRealPath == "" {
|
||||
glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: certFile "%s" does not exist`, certFile))
|
||||
}
|
||||
keyFileRealPath := gfile.RealPath(keyFile)
|
||||
if keyFileRealPath == "" {
|
||||
keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFileRealPath)
|
||||
keyFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + keyFile)
|
||||
if keyFileRealPath == "" {
|
||||
keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFile)
|
||||
}
|
||||
}
|
||||
if keyFileRealPath == "" {
|
||||
glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: keyFile "%s" does not exist`, keyFile))
|
||||
}
|
||||
s.config.HTTPSCertPath = certFileRealPath
|
||||
s.config.HTTPSKeyPath = keyFileRealPath
|
||||
if len(tlsConfig) > 0 {
|
||||
s.config.TLSConfig = tlsConfig[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 设置TLS配置对象
|
||||
func (s *Server)SetTLSConfig(tlsConfig tls.Config) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
|
||||
return
|
||||
}
|
||||
s.config.TLSConfig = tlsConfig
|
||||
}
|
||||
|
||||
// 设置http server参数 - ReadTimeout
|
||||
|
||||
@ -84,18 +84,22 @@ func (s *gracefulServer) setFd(fd int) {
|
||||
}
|
||||
|
||||
// 执行HTTPS监听
|
||||
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
|
||||
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig...*tls.Config) error {
|
||||
addr := s.httpServer.Addr
|
||||
config := &tls.Config{}
|
||||
if s.httpServer.TLSConfig != nil {
|
||||
config := (*tls.Config)(nil)
|
||||
if len(tlsConfig) > 0 {
|
||||
config = tlsConfig[0]
|
||||
} else if s.httpServer.TLSConfig != nil {
|
||||
*config = *s.httpServer.TLSConfig
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
err := error(nil)
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if len(config.Certificates) == 0 {
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()))
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
// Package greuseport provides Listen and Dial functions that set socket
|
||||
// options in order to be able to reuse ports. You should only use this
|
||||
// package if you know what SO_REUSEADDR and SO_REUSEPORT are.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// // listen on the same port.
|
||||
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
|
||||
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1234")
|
||||
//
|
||||
// // dial from the same port.
|
||||
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
|
||||
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1235")
|
||||
// c, _ := reuse.Dial("tcp", "127.0.0.1:1234", "127.0.0.1:1235")
|
||||
//
|
||||
// Note: cant dial self because tcp/ip stacks use 4-tuples to identify connections,
|
||||
// and doing so would clash.
|
||||
package greuseport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
Enabled = false
|
||||
listenConfig = net.ListenConfig {
|
||||
Control: Control,
|
||||
}
|
||||
)
|
||||
|
||||
// Listen listens at the given network and address. see net.Listen
|
||||
// Returns a net.Listener created from a file discriptor for a socket
|
||||
// with SO_REUSEPORT and SO_REUSEADDR option set.
|
||||
func Listen(network, address string) (net.Listener, error) {
|
||||
return listenConfig.Listen(context.Background(), network, address)
|
||||
}
|
||||
|
||||
// ListenPacket listens at the given network and address. see net.ListenPacket
|
||||
// Returns a net.Listener created from a file discriptor for a socket
|
||||
// with SO_REUSEPORT and SO_REUSEADDR option set.
|
||||
func ListenPacket(network, address string) (net.PacketConn, error) {
|
||||
return listenConfig.ListenPacket(context.Background(), network, address)
|
||||
}
|
||||
|
||||
// Dial dials the given network and address. see net.Dialer.Dial
|
||||
// Returns a net.Conn created from a file discriptor for a socket
|
||||
// with SO_REUSEPORT and SO_REUSEADDR option set.
|
||||
func Dial(network, laddr, raddr string) (net.Conn, error) {
|
||||
nla, err := ResolveAddr(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := net.Dialer {
|
||||
Control: Control,
|
||||
LocalAddr: nla,
|
||||
}
|
||||
return d.Dial(network, raddr)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package greuseport
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func ResolveAddr(network, address string) (net.Addr, error) {
|
||||
switch network {
|
||||
case "ip", "ip4", "ip6":
|
||||
return net.ResolveIPAddr(network, address)
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return net.ResolveTCPAddr(network, address)
|
||||
case "udp", "udp4", "udp6":
|
||||
return net.ResolveUDPAddr(network, address)
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
return net.ResolveUnixAddr(network, address)
|
||||
default:
|
||||
return nil, net.UnknownNetworkError(network)
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
// +build !windows,!linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd
|
||||
|
||||
package greuseport
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// See net.RawConn.Control
|
||||
func Control(network, address string, c syscall.RawConn) (err error) {
|
||||
return nil
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package greuseport
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/third/golang.org/x/sys/unix"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Enabled = true
|
||||
}
|
||||
|
||||
// See net.RawConn.Control
|
||||
func Control(network, address string, c syscall.RawConn) (err error) {
|
||||
c.Control(func(fd uintptr) {
|
||||
if err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
if err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
})
|
||||
return
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package greuseport
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/third/golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// See net.RawConn.Control
|
||||
func Control(network, address string, c syscall.RawConn) (err error) {
|
||||
return c.Control(func(fd uintptr) {
|
||||
if err = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,213 +0,0 @@
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package greuseport_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/net/greuseport"
|
||||
"html"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
httpServerOneResponse = "1"
|
||||
httpServerTwoResponse = "2"
|
||||
)
|
||||
|
||||
var (
|
||||
httpServerOne = NewHTTPServer(httpServerOneResponse)
|
||||
httpServerTwo = NewHTTPServer(httpServerTwoResponse)
|
||||
)
|
||||
|
||||
func NewHTTPServer(resp string) *httptest.Server {
|
||||
return httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, resp)
|
||||
}))
|
||||
}
|
||||
func TestNewReusablePortListener(t *testing.T) {
|
||||
listenerOne, err := greuseport.Listen("tcp4", "localhost:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerOne.Close()
|
||||
|
||||
listenerTwo, err := greuseport.Listen("tcp", "127.0.0.1:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerTwo.Close()
|
||||
|
||||
listenerThree, err := greuseport.Listen("tcp6", "[::]:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerThree.Close()
|
||||
|
||||
listenerFour, err := greuseport.Listen("tcp6", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerFour.Close()
|
||||
|
||||
listenerFive, err := greuseport.Listen("tcp4", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerFive.Close()
|
||||
|
||||
listenerSix, err := greuseport.Listen("tcp", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerSix.Close()
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
listenerOne, err := greuseport.Listen("tcp4", "localhost:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerOne.Close()
|
||||
|
||||
listenerTwo, err := greuseport.Listen("tcp", "127.0.0.1:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerTwo.Close()
|
||||
|
||||
listenerThree, err := greuseport.Listen("tcp6", "[::]:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerThree.Close()
|
||||
|
||||
listenerFour, err := greuseport.Listen("tcp6", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerFour.Close()
|
||||
|
||||
listenerFive, err := greuseport.Listen("tcp4", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerFive.Close()
|
||||
|
||||
listenerSix, err := greuseport.Listen("tcp", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerSix.Close()
|
||||
}
|
||||
|
||||
func TestNewReusablePortServers(t *testing.T) {
|
||||
listenerOne, err := greuseport.Listen("tcp4", "localhost:10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerOne.Close()
|
||||
|
||||
listenerTwo, err := greuseport.Listen("tcp6", ":10081")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer listenerTwo.Close()
|
||||
|
||||
httpServerOne.Listener = listenerOne
|
||||
httpServerTwo.Listener = listenerTwo
|
||||
|
||||
httpServerOne.Start()
|
||||
httpServerTwo.Start()
|
||||
|
||||
// Server One — First Response
|
||||
resp1, err := http.Get(httpServerOne.URL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
body1, err := ioutil.ReadAll(resp1.Body)
|
||||
resp1.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(body1) != httpServerOneResponse && string(body1) != httpServerTwoResponse {
|
||||
t.Errorf("Expected %#v or %#v, got %#v.", httpServerOneResponse, httpServerTwoResponse, string(body1))
|
||||
}
|
||||
|
||||
// Server Two — First Response
|
||||
resp2, err := http.Get(httpServerTwo.URL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
body2, err := ioutil.ReadAll(resp2.Body)
|
||||
resp1.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(body2) != httpServerOneResponse && string(body2) != httpServerTwoResponse {
|
||||
t.Errorf("Expected %#v or %#v, got %#v.", httpServerOneResponse, httpServerTwoResponse, string(body2))
|
||||
}
|
||||
|
||||
httpServerTwo.Close()
|
||||
|
||||
// Server One — Second Response
|
||||
resp3, err := http.Get(httpServerOne.URL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
body3, err := ioutil.ReadAll(resp3.Body)
|
||||
resp1.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(body3) != httpServerOneResponse {
|
||||
t.Errorf("Expected %#v, got %#v.", httpServerOneResponse, string(body3))
|
||||
}
|
||||
|
||||
// Server One — Third Response
|
||||
resp5, err := http.Get(httpServerOne.URL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
body5, err := ioutil.ReadAll(resp5.Body)
|
||||
resp1.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(body5) != httpServerOneResponse {
|
||||
t.Errorf("Expected %#v, got %#v.", httpServerOneResponse, string(body5))
|
||||
}
|
||||
|
||||
httpServerOne.Close()
|
||||
}
|
||||
|
||||
func BenchmarkNewReusablePortListener(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listener, err := greuseport.Listen("tcp", ":10081")
|
||||
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
} else {
|
||||
listener.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNewReusablePortListener() {
|
||||
listener, err := greuseport.Listen("tcp", ":8881")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
server := &http.Server{}
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println(os.Getgid())
|
||||
fmt.Fprintf(w, "Hello, %q\n", html.EscapeString(r.URL.Path))
|
||||
})
|
||||
|
||||
panic(server.Serve(listener))
|
||||
}
|
||||
77
g/os/gcfg/gcfg_z_unit_test.go
Normal file
77
g/os/gcfg/gcfg_z_unit_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gcfg_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/gcfg"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Basic(t *testing.T) {
|
||||
config := `
|
||||
v1 = 1
|
||||
v2 = "true"
|
||||
v3 = "off"
|
||||
v4 = "1.23"
|
||||
array = [1,2,3]
|
||||
[redis]
|
||||
disk = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
`
|
||||
gtest.Case(t, func() {
|
||||
path := "config.toml"
|
||||
err := gfile.PutContents(path, config)
|
||||
gtest.Assert(err, nil)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
c := gcfg.New(".")
|
||||
gtest.Assert(c.Get("v1"), 1)
|
||||
gtest.AssertEQ(c.GetInt("v1"), 1)
|
||||
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
|
||||
gtest.AssertEQ(c.GetInt16("v1"), int16(1))
|
||||
gtest.AssertEQ(c.GetInt32("v1"), int32(1))
|
||||
gtest.AssertEQ(c.GetInt64("v1"), int64(1))
|
||||
gtest.AssertEQ(c.GetUint("v1"), uint(1))
|
||||
gtest.AssertEQ(c.GetUint8("v1"), uint8(1))
|
||||
gtest.AssertEQ(c.GetUint16("v1"), uint16(1))
|
||||
gtest.AssertEQ(c.GetUint32("v1"), uint32(1))
|
||||
gtest.AssertEQ(c.GetUint64("v1"), uint64(1))
|
||||
|
||||
gtest.AssertEQ(c.GetVar("v1").String(), "1")
|
||||
gtest.AssertEQ(c.GetVar("v1").Bool(), true)
|
||||
gtest.AssertEQ(c.GetVar("v2").String(), "true")
|
||||
gtest.AssertEQ(c.GetVar("v2").Bool(), true)
|
||||
|
||||
gtest.AssertEQ(c.GetString("v1"), "1")
|
||||
gtest.AssertEQ(c.GetFloat32("v4"), float32(1.23))
|
||||
gtest.AssertEQ(c.GetFloat64("v4"), float64(1.23))
|
||||
gtest.AssertEQ(c.GetString("v2"), "true")
|
||||
gtest.AssertEQ(c.GetBool("v2"), true)
|
||||
gtest.AssertEQ(c.GetBool("v3"), false)
|
||||
|
||||
gtest.AssertEQ(c.Contains("v1"), true)
|
||||
gtest.AssertEQ(c.Contains("v2"), true)
|
||||
gtest.AssertEQ(c.Contains("v3"), true)
|
||||
gtest.AssertEQ(c.Contains("v4"), true)
|
||||
gtest.AssertEQ(c.Contains("v5"), false)
|
||||
|
||||
gtest.AssertEQ(c.GetInts("array"), []int{1,2,3})
|
||||
gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"})
|
||||
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"})
|
||||
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"})
|
||||
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
|
||||
"disk" : "127.0.0.1:6379,0",
|
||||
"cache" : "127.0.0.1:6379,1",
|
||||
})
|
||||
gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + "config.toml")
|
||||
|
||||
})
|
||||
}
|
||||
@ -29,7 +29,27 @@ var (
|
||||
defaultCron = New()
|
||||
)
|
||||
|
||||
// 添加执行方法,可以给定名字,以便于后续执行删除
|
||||
// 设置日志输出路径
|
||||
func SetLogPath(path string) {
|
||||
defaultCron.SetLogPath(path)
|
||||
}
|
||||
|
||||
// 获取设置的日志输出路径
|
||||
func GetLogPath() string {
|
||||
return defaultCron.GetLogPath()
|
||||
}
|
||||
|
||||
// 设置日志输出等级。
|
||||
func SetLogLevel(level int) {
|
||||
defaultCron.SetLogLevel(level)
|
||||
}
|
||||
|
||||
// 获取日志输出等级。
|
||||
func GetLogLevel() int {
|
||||
return defaultCron.GetLogLevel()
|
||||
}
|
||||
|
||||
// 添加定时任务,可以给定名字,以便于后续执行删除
|
||||
func Add(pattern string, job func(), name ... string) (*Entry, error) {
|
||||
return defaultCron.Add(pattern, job, name...)
|
||||
}
|
||||
|
||||
@ -12,26 +12,51 @@ import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gtimer"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定时任务管理对象
|
||||
type Cron struct {
|
||||
idgen *gtype.Int // 用于唯一名称生成
|
||||
status *gtype.Int // 定时任务状态(0: 未执行; 1: 运行中; 2: 已停止; -1:删除关闭)
|
||||
entries *gmap.StringInterfaceMap // 所有的定时任务项
|
||||
idGen *gtype.Int64 // 用于唯一名称生成
|
||||
status *gtype.Int // 定时任务状态(0: 未执行; 1: 运行中; 2: 已停止; -1:删除关闭)
|
||||
entries *gmap.StringInterfaceMap // 所有的定时任务项
|
||||
logPath *gtype.String // 日志文件输出目录
|
||||
logLevel *gtype.Int // 日志输出等级
|
||||
}
|
||||
|
||||
// 创建自定义的定时任务管理对象
|
||||
func New() *Cron {
|
||||
return &Cron {
|
||||
idgen : gtype.NewInt(1000000),
|
||||
status : gtype.NewInt(STATUS_RUNNING),
|
||||
entries : gmap.NewStringInterfaceMap(),
|
||||
idGen : gtype.NewInt64(),
|
||||
status : gtype.NewInt(STATUS_RUNNING),
|
||||
entries : gmap.NewStringInterfaceMap(),
|
||||
logPath : gtype.NewString(),
|
||||
logLevel : gtype.NewInt(glog.LEVEL_PROD),
|
||||
}
|
||||
}
|
||||
|
||||
// 设置日志输出路径
|
||||
func (c *Cron) SetLogPath(path string) {
|
||||
c.logPath.Set(path)
|
||||
}
|
||||
|
||||
// 获取设置的日志输出路径
|
||||
func (c *Cron) GetLogPath() string {
|
||||
return c.logPath.Val()
|
||||
}
|
||||
|
||||
// 设置日志输出等级。
|
||||
func (c *Cron) SetLogLevel(level int) {
|
||||
c.logLevel.Set(level)
|
||||
}
|
||||
|
||||
// 获取日志输出等级。
|
||||
func (c *Cron) GetLogLevel() int {
|
||||
return c.logLevel.Val()
|
||||
}
|
||||
|
||||
// 添加定时任务
|
||||
func (c *Cron) Add(pattern string, job func(), name ... string) (*Entry, error) {
|
||||
if len(name) > 0 {
|
||||
|
||||
@ -7,8 +7,11 @@
|
||||
package gcron
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gtimer"
|
||||
"strconv"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -17,6 +20,7 @@ type Entry struct {
|
||||
cron *Cron // 所属定时任务
|
||||
entry *gtimer.Entry // 定时器任务对象
|
||||
schedule *cronSchedule // 定时任务配置对象
|
||||
jobName string // 任务注册方法名称
|
||||
Name string // 定时任务名称
|
||||
Job func() // 注册定时任务方法
|
||||
Time time.Time // 注册时间
|
||||
@ -31,13 +35,14 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, times int, n
|
||||
entry := &Entry {
|
||||
cron : c,
|
||||
schedule : schedule,
|
||||
jobName : runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(),
|
||||
Job : job,
|
||||
Time : time.Now(),
|
||||
}
|
||||
if len(name) > 0 {
|
||||
entry.Name = name[0]
|
||||
} else {
|
||||
entry.Name = strconv.Itoa(c.idgen.Add(1))
|
||||
entry.Name = "gcron-" + gconv.String(c.idGen.Add(1))
|
||||
}
|
||||
entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, times, gtimer.STATUS_STOPPED)
|
||||
entry.entry.Start()
|
||||
@ -89,20 +94,29 @@ func (entry *Entry) Close() {
|
||||
// 定时任务检查执行
|
||||
func (entry *Entry) check() {
|
||||
if entry.schedule.meet(time.Now()) {
|
||||
path := entry.cron.GetLogPath()
|
||||
level := entry.cron.GetLogLevel()
|
||||
switch entry.cron.status.Val() {
|
||||
case STATUS_STOPPED:
|
||||
return
|
||||
|
||||
case STATUS_CLOSED:
|
||||
entry.cron.Remove(entry.Name)
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s remove", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
gtimer.Exit()
|
||||
|
||||
case STATUS_READY: fallthrough
|
||||
case STATUS_RUNNING:
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
defer func() {
|
||||
if entry.entry.Status() == STATUS_CLOSED {
|
||||
entry.cron.Remove(entry.Name)
|
||||
}
|
||||
if err := recover(); err != nil {
|
||||
glog.Path(path).Level(level).Errorfln("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err)
|
||||
} else {
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
}
|
||||
}()
|
||||
entry.Job()
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ import (
|
||||
|
||||
const (
|
||||
LEVEL_ALL = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
|
||||
LEVEL_DEV = LEVEL_ALL
|
||||
LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
|
||||
LEVEL_DEBU = 1 << iota
|
||||
LEVEL_INFO
|
||||
LEVEL_NOTI
|
||||
@ -141,6 +143,14 @@ func To(writer io.Writer) *Logger {
|
||||
return logger.To(writer)
|
||||
}
|
||||
|
||||
// Path is a chaining function,
|
||||
// which sets the directory path to <path> for current logging content output.
|
||||
//
|
||||
// 链式操作,设置下一次输出的日志路径。
|
||||
func Path(path string) *Logger {
|
||||
return logger.Path(path)
|
||||
}
|
||||
|
||||
// Cat is a chaining function,
|
||||
// which sets the category to <category> for current logging content output.
|
||||
//
|
||||
|
||||
@ -26,6 +26,23 @@ func (l *Logger) To(writer io.Writer) *Logger {
|
||||
return logger
|
||||
}
|
||||
|
||||
// Path is a chaining function,
|
||||
// which sets the directory path to <path> for current logging content output.
|
||||
//
|
||||
// 链式操作,设置下一次输出的日志路径。
|
||||
func (l *Logger) Path(path string) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
logger = l.Clone()
|
||||
} else {
|
||||
logger = l
|
||||
}
|
||||
if path != "" {
|
||||
logger.SetPath(path)
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// Cat is a chaining function,
|
||||
// which sets the category to <category> for current logging content output.
|
||||
//
|
||||
|
||||
@ -588,7 +588,7 @@ func HideStr(str string, percent int, hide string) string {
|
||||
buffer.WriteString(string(hideStr))
|
||||
buffer.WriteString(string(rs[start + hideLen : ]))
|
||||
if len(array) > 1 {
|
||||
buffer.WriteString(array[1])
|
||||
buffer.WriteString("@" + array[1])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
@ -46,11 +46,10 @@ func PosI(haystack, needle string, startOffset...int) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
haystack = haystack[offset : ]
|
||||
if offset < 0 {
|
||||
offset += length
|
||||
}
|
||||
pos := strings.Index(strings.ToLower(haystack), strings.ToLower(needle))
|
||||
pos := strings.Index(strings.ToLower(haystack[offset : ]), strings.ToLower(needle))
|
||||
if pos == -1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
@ -85,7 +85,9 @@ func Test_UcFirst(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
e1 := "AbcdEFG乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.UcFirst(""), "")
|
||||
gtest.Assert(gstr.UcFirst(s1), e1)
|
||||
gtest.Assert(gstr.UcFirst(e1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -93,7 +95,9 @@ func Test_LcFirst(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "AbcdEFG乱入的中文abcdefg"
|
||||
e1 := "abcdEFG乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.LcFirst(""), "")
|
||||
gtest.Assert(gstr.LcFirst(s1), e1)
|
||||
gtest.Assert(gstr.LcFirst(e1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -131,9 +135,11 @@ func Test_IsNumeric(t *testing.T) {
|
||||
|
||||
func Test_SubStr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 0), "我爱GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2), "GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2, 2), "Go")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 0), "我爱GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2), "GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2, 2), "Go")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", -1, 30), "我爱GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 30, 30), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -143,6 +149,7 @@ func Test_StrLimit(t *testing.T) {
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 2, ""), "我爱")
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 2, "**"), "我爱**")
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 4, ""), "我爱Go")
|
||||
gtest.Assert(gstr.StrLimit("*", 4, ""), "*")
|
||||
})
|
||||
}
|
||||
|
||||
@ -154,8 +161,9 @@ func Test_Reverse(t *testing.T) {
|
||||
|
||||
func Test_NumberFormat(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, ".", ","), "1,234,567.89")
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, "#", "/"), "1/234/567#89")
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, ".", ","), "1,234,567.89")
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, "#", "/"), "1/234/567#89")
|
||||
gtest.Assert(gstr.NumberFormat(-1234567.8910, 2, "#", "/"), "-1/234/567#89")
|
||||
})
|
||||
}
|
||||
|
||||
@ -163,6 +171,7 @@ func Test_ChunkSplit(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.ChunkSplit("1234", 1, "#"), "1#2#3#4#")
|
||||
gtest.Assert(gstr.ChunkSplit("我爱123", 1, "#"), "我#爱#1#2#3#")
|
||||
gtest.Assert(gstr.ChunkSplit("1234", 1, ""), "1\r\n2\r\n3\r\n4\r\n")
|
||||
})
|
||||
}
|
||||
|
||||
@ -204,6 +213,7 @@ func Test_CountChars(t *testing.T) {
|
||||
func Test_WordWrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.WordWrap("12 34", 2, "<br>"), "12<br>34")
|
||||
gtest.Assert(gstr.WordWrap("12 34", 2, "\n"), "12\n34")
|
||||
gtest.Assert(gstr.WordWrap("A very long woooooooooooooooooord. and something", 7, "<br>"),
|
||||
"A very<br>long<br>woooooooooooooooooord.<br>and<br>something")
|
||||
})
|
||||
@ -226,6 +236,8 @@ func Test_Repeat(t *testing.T) {
|
||||
func Test_Str(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Str("name@example.com", "@"), "@example.com")
|
||||
gtest.Assert(gstr.Str("name@example.com", ""), "")
|
||||
gtest.Assert(gstr.Str("name@example.com", "z"), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -274,7 +286,8 @@ func Test_Ord(t *testing.T) {
|
||||
|
||||
func Test_HideStr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.HideStr("15928008611", 40, "*"), "159****8611")
|
||||
gtest.Assert(gstr.HideStr("15928008611", 40, "*"), "159****8611")
|
||||
gtest.Assert(gstr.HideStr("john@kohg.cn", 40, "*"), "jo*n@kohg.cn")
|
||||
})
|
||||
}
|
||||
|
||||
@ -322,4 +335,51 @@ func Test_CountI(t *testing.T) {
|
||||
gtest.Assert(gstr.CountI(s, "b"), 1)
|
||||
gtest.Assert(gstr.CountI(s, "d"), 2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Compare(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Compare("a", "b"), -1)
|
||||
gtest.Assert(gstr.Compare("a", "a"), 0)
|
||||
gtest.Assert(gstr.Compare("b", "a"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Equal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Equal("a", "A"), true)
|
||||
gtest.Assert(gstr.Equal("a", "a"), true)
|
||||
gtest.Assert(gstr.Equal("b", "a"), false)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func Test_Contains(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Contains("abc", "a"), true)
|
||||
gtest.Assert(gstr.Contains("abc", "A"), false)
|
||||
gtest.Assert(gstr.Contains("abc", "ab"), true)
|
||||
gtest.Assert(gstr.Contains("abc", "abc"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ContainsI(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.ContainsI("abc", "a"), true)
|
||||
gtest.Assert(gstr.ContainsI("abc", "A"), true)
|
||||
gtest.Assert(gstr.ContainsI("abc", "Ab"), true)
|
||||
gtest.Assert(gstr.ContainsI("abc", "ABC"), true)
|
||||
gtest.Assert(gstr.ContainsI("abc", "ABCD"), false)
|
||||
gtest.Assert(gstr.ContainsI("abc", "D"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ContainsAny(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.ContainsAny("abc", "a"), true)
|
||||
gtest.Assert(gstr.ContainsAny("abc", "cd"), true)
|
||||
gtest.Assert(gstr.ContainsAny("abc", "de"), false)
|
||||
gtest.Assert(gstr.ContainsAny("abc", "A"), false)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -17,17 +17,21 @@ import (
|
||||
func Test_Pos(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
gtest.Assert(gstr.Pos(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.Pos(s1, "ab", 2), 7)
|
||||
gtest.Assert(gstr.Pos(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.Pos(s1, "ab", 2), 7)
|
||||
gtest.Assert(gstr.Pos(s1, "abd", 0), -1)
|
||||
gtest.Assert(gstr.Pos(s1, "e", -4), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PosI(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
gtest.Assert(gstr.PosI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosI(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.PosI(s1, "ef", 2), 4)
|
||||
gtest.Assert(gstr.PosI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosI(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.PosI(s1, "ef", 2), 4)
|
||||
gtest.Assert(gstr.PosI(s1, "abd", 0), -1)
|
||||
gtest.Assert(gstr.PosI(s1, "E", -4), 11)
|
||||
})
|
||||
}
|
||||
|
||||
@ -38,7 +42,9 @@ func Test_PosR(t *testing.T) {
|
||||
gtest.Assert(gstr.PosR(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosR(s1, "ab"), 7)
|
||||
gtest.Assert(gstr.PosR(s2, "ab", -2), 0)
|
||||
gtest.Assert(gstr.PosR(s1, "ef"), 11)
|
||||
gtest.Assert(gstr.PosR(s1, "ef"), 11)
|
||||
gtest.Assert(gstr.PosR(s1, "abd", 0), -1)
|
||||
gtest.Assert(gstr.PosR(s1, "e", -4), -1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -46,9 +52,11 @@ func Test_PosRI(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
s2 := "abcdEFGz1cdeab"
|
||||
gtest.Assert(gstr.PosRI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosRI(s1, "AB"), 7)
|
||||
gtest.Assert(gstr.PosRI(s2, "AB", -2), 0)
|
||||
gtest.Assert(gstr.PosRI(s1, "EF"), 11)
|
||||
gtest.Assert(gstr.PosRI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosRI(s1, "AB"), 7)
|
||||
gtest.Assert(gstr.PosRI(s2, "AB", -2), 0)
|
||||
gtest.Assert(gstr.PosRI(s1, "EF"), 11)
|
||||
gtest.Assert(gstr.PosRI(s1, "abd", 0), -1)
|
||||
gtest.Assert(gstr.PosRI(s1, "e", -5), 4)
|
||||
})
|
||||
}
|
||||
@ -79,10 +79,11 @@ func Bytes(i interface{}) []byte {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
if r, ok := i.([]byte); ok {
|
||||
return r
|
||||
} else {
|
||||
return gbinary.Encode(i)
|
||||
switch value := i.(type) {
|
||||
case string: return []byte(value)
|
||||
case []byte: return value
|
||||
default:
|
||||
return gbinary.Encode(i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,12 +16,12 @@ var (
|
||||
|
||||
// 随机计算是否满足给定的概率(分子/分母)
|
||||
func Meet(num, total int) bool {
|
||||
return Rand(0, total) <= num
|
||||
return Intn(total) < num
|
||||
}
|
||||
|
||||
// 随机计算是否满足给定的概率(float32)
|
||||
func MeetProb(prob float32) bool {
|
||||
return Rand(0, 1e7) <= int(prob*1e7)
|
||||
return Intn(1e7) < int(prob*1e7)
|
||||
}
|
||||
|
||||
// Rand 别名, 返回: [min, max]
|
||||
@ -92,6 +92,8 @@ func RandLetters(n int) string {
|
||||
}
|
||||
|
||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
|
||||
//
|
||||
// 返回[0, n)的随机数组成的slice。
|
||||
func Perm(n int) []int {
|
||||
m := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
@ -47,7 +47,7 @@ func init() {
|
||||
// 自定义的 rand.Intn ,绝对随机, 返回: [0, max)
|
||||
func Intn (max int) int {
|
||||
n := int(<- bufferChan)%max
|
||||
if n < 0 {
|
||||
if (max > 0 && n < 0) || (max < 0 && n > 0) {
|
||||
return -n
|
||||
}
|
||||
return n
|
||||
|
||||
146
g/util/grand/grand_z_unit_test.go
Normal file
146
g/util/grand/grand_z_unit_test.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package grand_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func Test_Intn(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 1000000; i++ {
|
||||
n := grand.Intn(100)
|
||||
gtest.AssertLT(n, 100)
|
||||
gtest.AssertGTE(n, 0)
|
||||
}
|
||||
for i := 0; i < 1000000; i++ {
|
||||
n := grand.Intn(-100)
|
||||
gtest.AssertLTE(n, 0)
|
||||
gtest.AssertGT(n, -100)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Meet(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.Meet(100, 100), true)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.Meet(0, 100), false)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.Meet(50, 100), []bool{true, false})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MeetProb(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.MeetProb(1), true)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.MeetProb(0), false)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.MeetProb(0.5), []bool{true, false})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_N(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.N(1, 1), 1)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.N(0, 0), 0)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.N(1, 2), []int{1, 2})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.Rand(1, 1), 1)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(grand.Rand(0, 0), 0)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.Rand(1, 2), []int{1, 2})
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.Rand(-1, 2), []int{-1, 0, 1, 2})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Str(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.Str(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RandStr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.RandStr(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Digits(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.Digits(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RandDigits(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.RandDigits(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Letters(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.Letters(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RandLetters(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.Assert(len(grand.RandLetters(5)), 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Perm(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
gtest.AssertIN(grand.Perm(5), []int{0,1,2,3,4})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -487,6 +487,22 @@ func getQueriedSqls() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
//data := g.Map{
|
||||
// "nickname" : "john",
|
||||
//}
|
||||
//db.SetDebug(true)
|
||||
//r, err := db.Table("user").Where("id=1").Data(data).Update()
|
||||
//fmt.Println(err)
|
||||
//fmt.Println(r.RowsAffected())
|
||||
|
||||
//data2 := g.Map{
|
||||
// "adsys1" : "ss",
|
||||
//}
|
||||
//db.SetDebug(true)
|
||||
//r, err := db.Table("cd_adsys").Where("adsys0=1").Data(data2).Update()
|
||||
//fmt.Println(err)
|
||||
//fmt.Println(r.RowsAffected())
|
||||
//return
|
||||
//db.SetDebug(true)
|
||||
//r, err := db.Table("test").Where("id=1").One()
|
||||
//fmt.Println(r["datetime"])
|
||||
|
||||
@ -9,7 +9,7 @@ func main() {
|
||||
s.BindHandler("/", func(r *ghttp.Request){
|
||||
r.Response.Writeln("来自于HTTPS的:哈喽世界!")
|
||||
})
|
||||
s.EnableHTTPS("/home/john/temp/server.crt", "/home/john/temp/server.key")
|
||||
s.EnableHTTPS("./server.crt", "./server.key")
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/net/greuseport"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 创建**两个**进程,并通过HTTP访问,查看返回结果。
|
||||
func main() {
|
||||
listener, err := greuseport.Listen("tcp", ":8881")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
server := &http.Server{}
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "gid: %d, pid: %d\n", os.Getgid(), os.Getpid())
|
||||
})
|
||||
|
||||
panic(server.Serve(listener))
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.5.16"
|
||||
const VERSION = "v1.5.17"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user