Example #1
0
// Returns Mailer if one is available or if new connection is allowed.
// For newly created mailers dialing the connection may still be
// in progress.
// If the pool is exhausted, the method blocks until a connection
// becomes available.
func (this *Pool) GetMailer(auth smtp.Auth) (res *Mailer, err error) {
	slog.Trace(1).Printe("Getting mailer", "auth", auth)
	this.cc.L.Lock()
	for {
		if this.clsd {
			err = errors.New("cannot check out from closed pool")
			break
		}
		slog.Trace(2).Printe("Trying pool", "auth", auth, "cnt", this.ccnt)
		if conn, ok := this.popMailer(auth); ok {
			res, err = this.newMailer(conn, auth)
			break
		}
		slog.Trace(2).Printe("Trying new", "auth", auth, "cnt", this.ccnt)
		if this.MaxConns == 0 || this.ccnt < this.MaxConns {
			res, err = this.newMailer(nil, auth)
			break
		}
		slog.Trace(2).Printe("Trying any", "auth", auth, "cnt", this.ccnt)
		if conn, ok := this.popMailer(nil); ok {
			res, err = this.newMailer(conn, auth)
			break
		}
		slog.Trace(2).Printe("Waiting", "auth", auth, "cnt", this.ccnt)
		this.cc.Wait()
	}
	this.cc.L.Unlock()
	if err != nil {
		slog.On(err).Trace(1).Prints("Getting mailer", "auth", auth)
	} else {
		slog.Success().Trace(1).Prints("Getting mailer", "auth", auth)
	}
	return
}
Example #2
0
// Puts Mailer back in the pool.
func (this *Pool) putMailer(mailer *Mailer) {
	slog.Trace(1).Printe("Putting mailer", "mailer", mailer)
	this.cc.L.Lock()
	defer this.cc.L.Unlock()
	conn := mailer.conn
	if conn != nil {
		conn = this.closeConnIfExp(conn)
	}
	if !this.clsd && conn != nil {
		// return to idle pool
		overflow := this.cpool.Push(conn)
		// this should be empty or only have one item,
		// unless pool resizing took place for some reason
		for _, v := range overflow {
			// TODO implement connection wait queue
			// and return a connection instead of closing it
			// if there are waiters
			this.closeConn(v)
		}
		if len(overflow) == 0 {
			this.cc.Signal()
		}
	}
	slog.Success().Trace(1).Prints("Putting mailer", "mailer", mailer)
}
Example #3
0
func (this *Pool) closeConnIfExp(conn *psmtpConn) *psmtpConn {
	slog.Trace(3).Printe("Checking connection", "conn", conn)
	now := time.Now()
	if !conn.ok || conn.etime.Before(now) {
		slog.With(errors.New("expired")).Trace(3).Prints("Checking connection", "conn", conn)
		this.closeConn(conn)
		return nil
	}
	slog.Success().Trace(3).Prints("Checking connection", "conn", conn)
	return conn
}
Example #4
0
func (this *Pool) closeConn(conn *psmtpConn) {
	if conn.ok {
		conn.ok = false
		if conn.client != nil {
			go func(c *psmtpConn, p *Pool) {
				c.client.Quit()
				p.cc.L.Lock()
				slog.Trace(3).Printe("Async closing connection", "conn", conn, "cnt", p.ccnt)
				p.ccnt--
				slog.Success().Trace(3).Prints("Async closing connection", "conn", conn, "cnt", p.ccnt)
				p.cc.L.Unlock()
				p.cc.Signal()
			}(conn, this)
			return
		}
	}
	slog.Trace(3).Printe("Closing connection", "conn", conn, "cnt", this.ccnt)
	this.ccnt--
	slog.Success().Trace(3).Prints("Closing connection", "conn", conn, "cnt", this.ccnt)
	this.cc.Signal()
}
Example #5
0
func (this *Pool) newMailer(conn *psmtpConn, auth smtp.Auth) (*Mailer, error) {
	if conn != nil && conn.auth == auth {
		return &Mailer{conn: conn, ctl: nil, pool: this}, nil
	}
	now := time.Now()
	if now.Sub(this.ftime) < this.MinWaitTime {
		return nil, errors.New("connect blackout period")
	}
	if conn == nil {
		slog.Trace(3).Printe("Incrementing for new", "cnt", this.ccnt)
		this.ccnt++
		slog.Success().Trace(3).Prints("Incrementing for new", "cnt", this.ccnt)
	}
	ctl := make(chan *connResp, 1)
	go func(ctl chan<- *connResp) {
		defer close(ctl)
		ok := false
		defer func() {
			if !ok {
				this.cc.L.Lock()
				slog.Trace(3).Printe("Decrementing on failure", "cnt", this.ccnt)
				this.ccnt--
				slog.Success().Trace(3).Prints("Decrementing on failure", "cnt", this.ccnt)
				this.cc.L.Unlock()
				this.cc.Signal()
			}
		}()
		if conn != nil && conn.client != nil {
			// TODO Convert to glog
			//log.Printf("Closing old connection")
			conn.client.Quit()
		}
		server := fmt.Sprintf("%s:%d", this.Host, this.Port)
		// TODO Convert to glog
		//log.Printf("Dialing %s...", server)
		if this.net_DialTimeout == nil {
			this.net_DialTimeout = net.DialTimeout
		}
		if this.tls_DialTimeout == nil {
			this.tls_DialTimeout = func(network string, addr string, timeout time.Duration, tlsc *tls.Config) (net.Conn, error) {
				dialer := &net.Dialer{Timeout: timeout}
				return tls.DialWithDialer(dialer, network, addr, tlsc)
			}
		}
		var l net.Conn
		var err error
		if this.Smtps {
			var tlsc tls.Config
			if this.TLSClientConfig != nil {
				tlsc = *this.TLSClientConfig
			}
			l, err = this.tls_DialTimeout("tcp", server, this.Timeout, &tlsc)
		} else {
			l, err = this.net_DialTimeout("tcp", server, this.Timeout)
		}
		if err != nil {
			// TODO Convert to glog
			//log.Printf("Error dialing %s: %v", server, err)
			this.ftime = now
			ctl <- &connResp{nil, err}
			return
		}
		if this.net_smtp_NewClient == nil {
			this.net_smtp_NewClient = func(conn net.Conn, server string) (smtpClient, error) {
				return smtp.NewClient(conn, server)
			}
		}
		c, err := this.net_smtp_NewClient(l, this.Host)
		if err != nil {
			this.ftime = now
			ctl <- &connResp{nil, err}
			return
		}
		hostname, _ := os.Hostname()
		if err = c.Hello(hostname); err != nil {
			c.Quit()
			ctl <- &connResp{nil, err}
			return
		}
		if this.TLSClientConfig != nil && !this.Smtps {
			tlsc := *this.TLSClientConfig
			// TODO Convert to glog
			//log.Printf("Starting TLS on %s...", server)
			if err = c.StartTLS(&tlsc); err != nil {
				this.ftime = now
				c.Quit()
				ctl <- &connResp{nil, err}
				return
			}
		}
		if auth != nil {
			// TODO Convert to glog
			//log.Printf("Authenticating with %s...", server)
			if err = c.Auth(auth); err != nil {
				c.Quit()
				ctl <- &connResp{nil, err}
				return
			}
		}
		ok = true
		ctl <- &connResp{
			conn: &psmtpConn{
				client: c,
				ok:     true,
				auth:   auth,
				ctime:  now,
				etime:  now.Add(this.MaxLiveTime),
				atime:  now,
			},
			err: nil,
		}
	}(ctl)
	return &Mailer{
		conn: nil,
		ctl:  ctl,
		pool: this,
	}, nil
}