package client import ( "crypto/tls" "net" "sync" "git.loafle.net/commons_go/logging" cuc "git.loafle.net/commons_go/util/context" ) 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() network := sb.GetNetwork() address := sb.GetAddress() conn, err := sb.Dial(network, address) if nil != err { return nil, err } tlsConfig := sb.GetTLSConfig() if nil != tlsConfig { cfg := tlsConfig.Clone() tlsConn := tls.Client(conn, cfg) if err := tlsConn.Handshake(); err != nil { tlsConn.Close() return nil, err } if !cfg.InsecureSkipVerify { if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { return nil, err } } conn = tlsConn } sh.OnConnect(sc, conn) s := retainSocket() s.Conn = conn 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) }