Skip to content

Commit 68208fe

Browse files
committed
client: add new functions to specify options
Expose a new set of functions to create a client with custom options. Signed-off-by: Robin Jarry <robin@jarry.cc>
1 parent b63eede commit 68208fe

File tree

1 file changed

+85
-14
lines changed

1 file changed

+85
-14
lines changed

client.go

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,29 @@ type Client struct {
4444
DebugWriter io.Writer
4545
}
4646

47+
// Options used when creating an SMTP client. If a field is left to its zero
48+
// value, the default will be used instead.
49+
type ClientOptions struct {
50+
// the name to use in HELO/EHLO/LHLO (default: "localhost")
51+
LocalName string
52+
// Time to wait for command responses (this includes 3xx reply to DATA)
53+
// (default: 5 minutes)
54+
CommandTimeout time.Duration
55+
// Time to wait for responses after final dot (default 12 minutes).
56+
SubmissionTimeout time.Duration
57+
}
58+
59+
const (
60+
DefaultLocalName = "localhost"
61+
// As recommended by RFC 5321. For DATA command reply (3xx one) RFC
62+
// recommends a slightly shorter timeout but we do not bother
63+
// differentiating these.
64+
DefaultCommandTimeout = 5 * time.Minute
65+
// 10 minutes + 2 minute buffer in case the server is doing transparent
66+
// forwarding and also follows recommended timeouts.
67+
DefaultSubmissionTimeout = 12 * time.Minute
68+
)
69+
4770
// 30 seconds was chosen as it's the same duration as http.DefaultTransport's
4871
// timeout.
4972
var defaultDialer = net.Dialer{Timeout: 30 * time.Second}
@@ -54,11 +77,17 @@ var defaultDialer = net.Dialer{Timeout: 30 * time.Second}
5477
// This function returns a plaintext connection. To enable TLS, use
5578
// DialStartTLS.
5679
func Dial(addr string) (*Client, error) {
80+
return DialWithOptions(addr, nil)
81+
}
82+
83+
// DialWithOpts returns a new Client connected to an SMTP server at addr using
84+
// custom options instead of the defaults.
85+
func DialWithOptions(addr string, opts *ClientOptions) (*Client, error) {
5786
conn, err := defaultDialer.Dial("tcp", addr)
5887
if err != nil {
5988
return nil, err
6089
}
61-
client := NewClient(conn)
90+
client := NewClientWithOptions(conn, opts)
6291
client.serverName, _, _ = net.SplitHostPort(addr)
6392
return client, nil
6493
}
@@ -68,6 +97,14 @@ func Dial(addr string) (*Client, error) {
6897
//
6998
// A nil tlsConfig is equivalent to a zero tls.Config.
7099
func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
100+
return DialTLSWithOptions(addr, tlsConfig, nil)
101+
}
102+
103+
// DialTLSWithOpts returns a new Client connected to an SMTP server via TLS at
104+
// addr using custom options instead of the defaults.
105+
func DialTLSWithOptions(
106+
addr string, tlsConfig *tls.Config, opts *ClientOptions,
107+
) (*Client, error) {
71108
tlsDialer := tls.Dialer{
72109
NetDialer: &defaultDialer,
73110
Config: tlsConfig,
@@ -76,7 +113,7 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
76113
if err != nil {
77114
return nil, err
78115
}
79-
client := NewClient(conn)
116+
client := NewClientWithOptions(conn, opts)
80117
client.serverName, _, _ = net.SplitHostPort(addr)
81118
return client, nil
82119
}
@@ -86,7 +123,15 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
86123
//
87124
// A nil tlsConfig is equivalent to a zero tls.Config.
88125
func DialStartTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
89-
c, err := Dial(addr)
126+
return DialStartTLSWithOptions(addr, tlsConfig, nil)
127+
}
128+
129+
// DialStartTLSWithOpts retruns a new Client connected to an SMTP server via
130+
// STARTTLS at addr using custom options instead of the defaults.
131+
func DialStartTLSWithOptions(
132+
addr string, tlsConfig *tls.Config, opts *ClientOptions,
133+
) (*Client, error) {
134+
c, err := DialWithOptions(addr, opts)
90135
if err != nil {
91136
return nil, err
92137
}
@@ -100,25 +145,45 @@ func DialStartTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
100145
// NewClient returns a new Client using an existing connection and host as a
101146
// server name to be used when authenticating.
102147
func NewClient(conn net.Conn) *Client {
148+
return NewClientWithOptions(conn, nil)
149+
}
150+
151+
// NewClient returns a new Client using an existing connection using custom
152+
// options instead of the defaults.
153+
func NewClientWithOptions(conn net.Conn, opts *ClientOptions) *Client {
154+
if opts == nil {
155+
opts = new(ClientOptions)
156+
}
103157
c := &Client{
104-
localName: "localhost",
105-
// As recommended by RFC 5321. For DATA command reply (3xx one) RFC
106-
// recommends a slightly shorter timeout but we do not bother
107-
// differentiating these.
108-
CommandTimeout: 5 * time.Minute,
109-
// 10 minutes + 2 minute buffer in case the server is doing transparent
110-
// forwarding and also follows recommended timeouts.
111-
SubmissionTimeout: 12 * time.Minute,
158+
localName: opts.LocalName,
159+
CommandTimeout: opts.CommandTimeout,
160+
SubmissionTimeout: opts.SubmissionTimeout,
161+
}
162+
if c.localName == "" {
163+
c.localName = DefaultLocalName
164+
}
165+
if c.CommandTimeout == 0 {
166+
c.CommandTimeout = DefaultCommandTimeout
167+
}
168+
if c.SubmissionTimeout == 0 {
169+
c.SubmissionTimeout = DefaultSubmissionTimeout
112170
}
113-
114171
c.setConn(conn)
115172

116173
return c
117174
}
118175

119176
// NewClientStartTLS creates a new Client and performs a STARTTLS command.
120177
func NewClientStartTLS(conn net.Conn, tlsConfig *tls.Config) (*Client, error) {
121-
c := NewClient(conn)
178+
return NewClientStartTLSWithOptions(conn, tlsConfig, nil)
179+
}
180+
181+
// NewClientStartTLS creates a new Client and performs a STARTTLS command.
182+
// It allows using custom options instead of the defaults.
183+
func NewClientStartTLSWithOptions(
184+
conn net.Conn, tlsConfig *tls.Config, opts *ClientOptions,
185+
) (*Client, error) {
186+
c := NewClientWithOptions(conn, opts)
122187
if err := initStartTLS(c, tlsConfig); err != nil {
123188
c.Close()
124189
return nil, err
@@ -142,7 +207,13 @@ func initStartTLS(c *Client, tlsConfig *tls.Config) error {
142207
// NewClientLMTP returns a new LMTP Client (as defined in RFC 2033) using an
143208
// existing connection and host as a server name to be used when authenticating.
144209
func NewClientLMTP(conn net.Conn) *Client {
145-
c := NewClient(conn)
210+
return NewClientLMTPWithOptions(conn, nil)
211+
}
212+
213+
// NewClientLMTP returns a new LMTP Client (as defined in RFC 2033) using
214+
// custom options instead of the defaults.
215+
func NewClientLMTPWithOptions(conn net.Conn, opts *ClientOptions) *Client {
216+
c := NewClientWithOptions(conn, opts)
146217
c.lmtp = true
147218
return c
148219
}

0 commit comments

Comments
 (0)