mirror of
https://github.com/pgsty/minio.git
synced 2026-09-15 23:14:04 +03:00
055030ea53
Signed-off-by: Feng Ruohang <rh@vonng.com> (cherry picked from commit 0d48d32d7e038ae1ea5966f3d7e0cb86780a6311) Signed-off-by: Feng Ruohang <rh@vonng.com>
138 lines
5.0 KiB
Diff
138 lines
5.0 KiB
Diff
diff --git a/cmd/server-main.go b/cmd/server-main.go
|
|
index 48ed0f87d..c8a3a19ca 100644
|
|
--- a/cmd/server-main.go
|
|
+++ b/cmd/server-main.go
|
|
@@ -901,6 +901,8 @@ func serverMain(ctx *cli.Context) {
|
|
close(globalGridStart)
|
|
close(globalLockGridStart)
|
|
|
|
+ // The HTTP/1 listener preserves absolute header deadlines and renews the
|
|
+ // body read/write idle limits, so transfers may outlast IdleTimeout.
|
|
httpServer := xhttp.NewServer(getServerListenAddrs()).
|
|
UseHandler(setCriticalErrorHandler(corsHandler(handler))).
|
|
UseTLSConfig(newTLSConfig(getCert)).
|
|
diff --git a/internal/deadlineconn/deadlineconn.go b/internal/deadlineconn/deadlineconn.go
|
|
index 95bb43eff..5fa5a1403 100644
|
|
--- a/internal/deadlineconn/deadlineconn.go
|
|
+++ b/internal/deadlineconn/deadlineconn.go
|
|
@@ -34,6 +34,8 @@ type DeadlineConn struct {
|
|
net.Conn
|
|
readDeadline time.Duration // sets the read deadline on a connection.
|
|
readSetAt time.Time
|
|
+ readExplicit time.Time // last deadline requested by the caller.
|
|
+ readDeadlineStrict bool // idle renewal must not extend readExplicit.
|
|
writeDeadline time.Duration // sets the write deadline on a connection.
|
|
writeSetAt time.Time
|
|
abortReads, abortWrites atomic.Bool // A deadline was set to indicate caller wanted the conn to time out.
|
|
@@ -59,17 +61,31 @@ func (c *DeadlineConn) setReadDeadline() {
|
|
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
- if c.abortReads.Load() {
|
|
+ if c.abortReads.Load() || c.infReads.Load() {
|
|
return
|
|
}
|
|
|
|
now := time.Now()
|
|
if now.Sub(c.readSetAt) > updateInterval {
|
|
- c.Conn.SetReadDeadline(now.Add(c.readDeadline + updateInterval))
|
|
+ deadline := now.Add(c.readDeadline + updateInterval)
|
|
+ if c.readDeadlineStrict && !c.readExplicit.IsZero() && c.readExplicit.Before(deadline) {
|
|
+ deadline = c.readExplicit
|
|
+ }
|
|
+ c.Conn.SetReadDeadline(deadline)
|
|
c.readSetAt = now
|
|
}
|
|
}
|
|
|
|
+// SetReadDeadlineStrict controls whether idle renewal may extend a deadline set
|
|
+// by SetReadDeadline or SetDeadline. The default is false. Explicit zero and
|
|
+// past deadlines retain their disable/cancel semantics in either mode.
|
|
+func (c *DeadlineConn) SetReadDeadlineStrict(strict bool) {
|
|
+ c.mu.Lock()
|
|
+ defer c.mu.Unlock()
|
|
+ c.readDeadlineStrict = strict
|
|
+ c.readSetAt = time.Time{}
|
|
+}
|
|
+
|
|
func (c *DeadlineConn) setWriteDeadline() {
|
|
// Do not set a Write deadline, if upstream wants to cancel all reads.
|
|
if c.writeDeadline <= 0 || c.abortWrites.Load() || c.infWrites.Load() {
|
|
@@ -115,6 +131,7 @@ func (c *DeadlineConn) SetDeadline(t time.Time) error {
|
|
defer c.mu.Unlock()
|
|
|
|
c.readSetAt = time.Time{}
|
|
+ c.readExplicit = t
|
|
c.writeSetAt = time.Time{}
|
|
c.abortReads.Store(!t.IsZero() && time.Until(t) < 0)
|
|
c.abortWrites.Store(!t.IsZero() && time.Until(t) < 0)
|
|
@@ -132,6 +149,7 @@ func (c *DeadlineConn) SetReadDeadline(t time.Time) error {
|
|
c.abortReads.Store(!t.IsZero() && time.Until(t) < 0)
|
|
c.infReads.Store(t.IsZero())
|
|
c.readSetAt = time.Time{}
|
|
+ c.readExplicit = t
|
|
return c.Conn.SetReadDeadline(t)
|
|
}
|
|
|
|
diff --git a/internal/http/listener.go b/internal/http/listener.go
|
|
index bc6de3af9..14d34f6ea 100644
|
|
--- a/internal/http/listener.go
|
|
+++ b/internal/http/listener.go
|
|
@@ -70,7 +70,10 @@ func (listener *httpListener) Accept() (conn net.Conn, err error) {
|
|
if result.err != nil {
|
|
return nil, result.err
|
|
}
|
|
- return deadlineconn.New(result.conn).WithReadDeadline(listener.opts.IdleTimeout).WithWriteDeadline(listener.opts.IdleTimeout), result.err
|
|
+ conn := deadlineconn.New(result.conn).WithReadDeadline(listener.opts.IdleTimeout).WithWriteDeadline(listener.opts.IdleTimeout)
|
|
+ // Server.Init switches to rolling reads only after HTTP/1 headers are read.
|
|
+ conn.SetReadDeadlineStrict(true)
|
|
+ return conn, nil
|
|
case <-listener.ctxDoneCh:
|
|
}
|
|
return nil, syscall.EINVAL
|
|
diff --git a/internal/http/server.go b/internal/http/server.go
|
|
index 2934fda6c..d9c19a33f 100644
|
|
--- a/internal/http/server.go
|
|
+++ b/internal/http/server.go
|
|
@@ -29,6 +29,7 @@ import (
|
|
"time"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
+ "github.com/minio/minio/internal/deadlineconn"
|
|
)
|
|
|
|
var (
|
|
@@ -123,6 +124,32 @@ func (srv *Server) Init(listenCtx context.Context, listenErrCallback func(listen
|
|
srv.listener = listener
|
|
srv.listenerMutex.Unlock()
|
|
|
|
+ connState := srv.ConnState
|
|
+ srv.ConnState = func(conn net.Conn, state http.ConnState) {
|
|
+ raw := conn
|
|
+ if tlsConn, ok := raw.(*tls.Conn); ok {
|
|
+ if tlsConn.ConnectionState().NegotiatedProtocol == "h2" {
|
|
+ // HTTP/2 owns its stream deadlines; do not change the connection.
|
|
+ raw = nil
|
|
+ } else {
|
|
+ raw = tlsConn.NetConn()
|
|
+ }
|
|
+ }
|
|
+ if dc, ok := raw.(*deadlineconn.DeadlineConn); ok {
|
|
+ switch state {
|
|
+ case http.StateNew, http.StateIdle:
|
|
+ dc.SetReadDeadlineStrict(true)
|
|
+ case http.StateActive:
|
|
+ // net/http has finished reading the headers, including buffered
|
|
+ // requests. Keep ReadTimeout as a rolling idle limit for uploads.
|
|
+ dc.SetReadDeadlineStrict(false)
|
|
+ }
|
|
+ }
|
|
+ if connState != nil {
|
|
+ connState(conn, state)
|
|
+ }
|
|
+ }
|
|
+
|
|
var l net.Listener = listener
|
|
if tlsConfig != nil {
|
|
l = tls.NewListener(listener, tlsConfig)
|