diff --git a/client/socket.go b/client/socket.go new file mode 100644 index 0000000..eb8d14b --- /dev/null +++ b/client/socket.go @@ -0,0 +1,98 @@ +package client + +import ( + "crypto/tls" + "net" + "sync" + + "git.loafle.net/commons_go/logging" +) + +type Socket interface { + Context() SocketContext + + net.Conn +} + +func NewSocket(sb SocketBuilder, parentContext cuc.Context) (Socket, error) { + if nil == sb { + logging.Logger().Panic("Client Socket: SocketBuilder must be specified") + } + sb.Validate() + + sc := sb.SocketContext(parentContext) + if nil == sc { + logging.Logger().Panic("Client Socket: SocketContext must be specified") + } + + sh := sb.SocketHandler() + if nil == sh { + logging.Logger().Panic("Client Socket: SocketHandler must be specified") + } + sh.Validate() + + d := &net.Dialer{} + d.Timeout = sb.GetTimeout() + d.KeepAlive = sb.GetKeepAlive() + d.LocalAddr = sb.GetLocalAddress() + + network := sb.GetNetwork() + address := sb.GetAddress() + tlsConfig := sb.GetTLSConfig() + + var conn net.Conn + var err error + + if nil == tlsConfig { + conn, err = d.Dial(network, address) + } else { + conn, err = tls.DialWithDialer(d, network, address, tlsConfig) + } + + if nil != err { + return nil, err + } + + sh.OnConnect(sc, conn) + + s := retainSocket() + s.ctx = sc + s.sh = sh + + return s, nil +} + +type netSocket struct { + net.Conn + + ctx SocketContext + sh SocketHandler +} + +func (s *netSocket) Context() SocketContext { + return s.ctx +} + +func (s *netSocket) Close() error { + err := s.Conn.Close() + s.sh.OnDisconnect(s) + releaseSocket(s) + return err +} + +var socketPool sync.Pool + +func retainSocket() *netSocket { + v := socketPool.Get() + if v == nil { + return &netSocket{} + } + return v.(*netSocket) +} + +func releaseSocket(s *netSocket) { + s.sh = nil + s.ctx = nil + + socketPool.Put(s) +} diff --git a/client/socket_builder.go b/client/socket_builder.go new file mode 100644 index 0000000..8de6f86 --- /dev/null +++ b/client/socket_builder.go @@ -0,0 +1,31 @@ +package client + +import ( + "crypto/tls" + "net" + "time" + + cuc "git.loafle.net/commons_go/util/context" +) + +type SocketBuilder interface { + SocketContext(parent cuc.Context) SocketContext + SocketHandler() SocketHandler + + GetNetwork() string + GetAddress() string + GetTLSConfig() *tls.Config + + GetKeepAlive() time.Duration + GetTimeout() time.Duration + GetLocalAddress() net.Addr + + // Validate is check handler value + // If you override ths method, must call + // + // func (sh *SocketHandlers) Validate() { + // sh.SocketHandlers.Validate() + // ... + // } + Validate() +} diff --git a/client/socket_builders.go b/client/socket_builders.go new file mode 100644 index 0000000..e8de870 --- /dev/null +++ b/client/socket_builders.go @@ -0,0 +1,70 @@ +package client + +import ( + "crypto/tls" + "net" + "time" + + "git.loafle.net/commons_go/logging" + cs "git.loafle.net/commons_go/server" +) + +type SocketBuilders struct { + Network string + Address string + TLSConfig *tls.Config + + KeepAlive time.Duration + Timeout time.Duration + + LocalAddress net.Addr +} + +func (sb *SocketBuilders) SocketContext(parent cuc.Context) SocketContext { + return newSocketContext(parent) +} + +func (sb *SocketBuilders) SocketHandler() SocketHandler { + return NewSocketHandler() +} + +func (sb *SocketBuilders) GetNetwork() string { + return sb.NewSocket +} + +func (sb *SocketBuilders) GetAddress() string { + return sb.Address +} + +func (sb *SocketBuilders) GetTLSConfig() *tls.Config { + return sb.TLSConfig +} + +func (sb *SocketBuilders) GetKeepAlive() time.Duration { + return sb.KeepAlive +} + +func (sb *SocketBuilders) GetTimeout() time.Duration { + return sb.Timeout +} + +func (sb *SocketBuilders) GetLocalAddress() Addr { + return sb.LocalAddress +} + +func (sb *SocketBuilders) Validate() { + if "" == sb.Network { + logging.Logger().Panic("Client Socket: Network must be specified") + } + if "" == sb.Address { + logging.Logger().Panic("Client Socket: Address must be specified") + } + + if 0 >= sb.KeepAlive { + sb.KeepAlive = cs.DefaultKeepAlive + } + if 0 >= sb.Timeout { + sb.Timeout = cs.DefaultConnectTimeout + } + +} diff --git a/client/socket_context.go b/client/socket_context.go new file mode 100644 index 0000000..07801fa --- /dev/null +++ b/client/socket_context.go @@ -0,0 +1,20 @@ +package client + +import ( + cuc "git.loafle.net/commons_go/util/context" +) + +type SocketContext interface { + cuc.Context +} + +func newSocketContext(parent cuc.Context) SocketContext { + sCTX := &socketContext{} + sCTX.Context = cuc.NewContext(parent) + + return sCTX +} + +type socketContext struct { + cuc.Context +} diff --git a/client/socket_handler.go b/client/socket_handler.go new file mode 100644 index 0000000..fd6cbf5 --- /dev/null +++ b/client/socket_handler.go @@ -0,0 +1,17 @@ +package client + +import "net" + +type SocketHandler interface { + OnConnect(socketContext SocketContext, conn net.Conn) + OnDisconnect(soc Socket) + + // Validate is check handler value + // If you override ths method, must call + // + // func (sh *SocketHandlers) Validate() { + // sh.SocketHandlers.Validate() + // ... + // } + Validate() +} diff --git a/client/socket_handlers.go b/client/socket_handlers.go new file mode 100644 index 0000000..b60733e --- /dev/null +++ b/client/socket_handlers.go @@ -0,0 +1,21 @@ +package client + +import "net" + +type SocketHandlers struct { +} + +func (sh *SocketHandlers) OnConnect(socketContext SocketContext, conn net.Conn) { + // no op +} + +func (sh *SocketHandlers) OnDisconnect(soc Socket) { + // no op +} + +func (sh *SocketHandlers) Validate() { +} + +func NewSocketHandler() SocketHandler { + return &SocketHandlers{} +} diff --git a/constants.go b/constants.go index 5a0d09c..10422a6 100644 --- a/constants.go +++ b/constants.go @@ -30,4 +30,8 @@ const ( DefaultReadTimeout = 0 // DefaultWriteTimeout is default value of write timeout DefaultWriteTimeout = 0 + + DefaultConnectTimeout = 0 + + DefaultKeepAlive = 0 )