mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 08:53:48 +03:00
smtpserver: when writing slow responses, don't take so long the remote smtp client regards it as timeout
when writing the 4xx temporary error line, we were taking 1s in between each byte. the total line could take longer than 30 seconds, which is the timeout we use for reading a whole line (regardless of individual bytes). so mox as deliverer was timing out to mox as slow rejecter. this causes slow writes to not take longer than the 30s timeout: if we are 2s before the 30s, we write the remainder in one go. based on a debug log from naturalethic, thanks!
This commit is contained in:
parent
2710a5b971
commit
fbc18d522d
1 changed files with 19 additions and 9 deletions
|
@ -412,17 +412,21 @@ func (c *conn) Write(buf []byte) (int, error) {
|
||||||
chunk = 1
|
chunk = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var n int
|
|
||||||
for len(buf) > 0 {
|
|
||||||
// We set a single deadline for Write and Read. This may be a TLS connection.
|
// We set a single deadline for Write and Read. This may be a TLS connection.
|
||||||
// SetDeadline works on the underlying connection. If we wouldn't touch the read
|
// SetDeadline works on the underlying connection. If we wouldn't touch the read
|
||||||
// deadline, and only set the write deadline and do a bunch of writes, the TLS
|
// deadline, and only set the write deadline and do a bunch of writes, the TLS
|
||||||
// library would still have to do reads on the underlying connection, and may reach
|
// library would still have to do reads on the underlying connection, and may reach
|
||||||
// a read deadline that was set for some earlier read.
|
// a read deadline that was set for some earlier read.
|
||||||
if err := c.conn.SetDeadline(c.earliestDeadline(30 * time.Second)); err != nil {
|
// We have one deadline for the whole write. In case of slow writing, we'll write
|
||||||
|
// the last chunk in one go, so remote smtp clients don't abort the connection for
|
||||||
|
// being slow.
|
||||||
|
deadline := c.earliestDeadline(30 * time.Second)
|
||||||
|
if err := c.conn.SetDeadline(deadline); err != nil {
|
||||||
c.log.Errorx("setting deadline for write", err)
|
c.log.Errorx("setting deadline for write", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
for len(buf) > 0 {
|
||||||
nn, err := c.conn.Write(buf[:chunk])
|
nn, err := c.conn.Write(buf[:chunk])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("write: %s (%w)", err, errIO))
|
panic(fmt.Errorf("write: %s (%w)", err, errIO))
|
||||||
|
@ -431,6 +435,12 @@ func (c *conn) Write(buf []byte) (int, error) {
|
||||||
buf = buf[chunk:]
|
buf = buf[chunk:]
|
||||||
if len(buf) > 0 && badClientDelay > 0 {
|
if len(buf) > 0 && badClientDelay > 0 {
|
||||||
mox.Sleep(mox.Context, badClientDelay)
|
mox.Sleep(mox.Context, badClientDelay)
|
||||||
|
|
||||||
|
// Make sure we don't take too long, otherwise the remote SMTP client may close the
|
||||||
|
// connection.
|
||||||
|
if time.Until(deadline) < 2*badClientDelay {
|
||||||
|
chunk = len(buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
|
|
Loading…
Reference in a new issue