Add response header timeouts (#9170)

- Add conservative timeouts upto 3 minutes
  for internode communication
- Add aggressive timeouts of 30 seconds
  for gateway communication

Fixes #9105
Fixes #8732
Fixes #8881
Fixes #8376
Fixes #9028
master
Harshavardhana 5 years ago committed by GitHub
parent 27b8f18cce
commit 3d3beb6a9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      cmd/admin-handlers.go
  2. 23
      cmd/config-current.go
  3. 4
      cmd/gateway/azure/gateway-azure.go
  4. 8
      cmd/gateway/b2/gateway-b2.go
  5. 4
      cmd/gateway/gcs/gateway-gcs.go
  6. 4
      cmd/gateway/oss/gateway-oss.go
  7. 4
      cmd/gateway/s3/gateway-s3.go
  8. 4
      cmd/generic-handlers.go
  9. 3
      cmd/notification.go
  10. 2
      cmd/object-handlers.go
  11. 38
      cmd/storage-rest-server.go
  12. 19
      cmd/utils.go
  13. 3
      cmd/web-handlers.go
  14. 2
      staticcheck.conf

@ -1533,7 +1533,7 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque
func fetchLambdaInfo(cfg config.Config) []map[string][]madmin.TargetIDStatus { func fetchLambdaInfo(cfg config.Config) []map[string][]madmin.TargetIDStatus {
// Fetch the targets // Fetch the targets
targetList, err := notify.RegisterNotificationTargets(cfg, GlobalServiceDoneCh, NewCustomHTTPTransport(), nil, true) targetList, err := notify.RegisterNotificationTargets(cfg, GlobalServiceDoneCh, NewGatewayHTTPTransport(), nil, true)
if err != nil { if err != nil {
return nil return nil
} }

@ -251,7 +251,7 @@ func validateConfig(s config.Config) error {
} }
} }
{ {
kmsCfg, err := crypto.LookupConfig(s, globalCertsCADir.Get(), NewCustomHTTPTransport()) kmsCfg, err := crypto.LookupConfig(s, globalCertsCADir.Get(), NewGatewayHTTPTransport())
if err != nil { if err != nil {
return err return err
} }
@ -269,7 +269,7 @@ func validateConfig(s config.Config) error {
} }
if _, err := openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default], if _, err := openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil { NewGatewayHTTPTransport(), xhttp.DrainBody); err != nil {
return err return err
} }
@ -279,7 +279,7 @@ func validateConfig(s config.Config) error {
} }
if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default], if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil { NewGatewayHTTPTransport(), xhttp.DrainBody); err != nil {
return err return err
} }
@ -287,7 +287,7 @@ func validateConfig(s config.Config) error {
return err return err
} }
return notify.TestNotificationTargets(s, GlobalServiceDoneCh, NewCustomHTTPTransport(), return notify.TestNotificationTargets(s, GlobalServiceDoneCh, NewGatewayHTTPTransport(),
globalNotificationSys.ConfiguredTargetIDs()) globalNotificationSys.ConfiguredTargetIDs())
} }
@ -362,7 +362,7 @@ func lookupConfigs(s config.Config) {
} }
} }
kmsCfg, err := crypto.LookupConfig(s, globalCertsCADir.Get(), NewCustomHTTPTransport()) kmsCfg, err := crypto.LookupConfig(s, globalCertsCADir.Get(), NewGatewayHTTPTransport())
if err != nil { if err != nil {
logger.LogIf(ctx, fmt.Errorf("Unable to setup KMS config: %w", err)) logger.LogIf(ctx, fmt.Errorf("Unable to setup KMS config: %w", err))
} }
@ -381,13 +381,13 @@ func lookupConfigs(s config.Config) {
} }
globalOpenIDConfig, err = openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default], globalOpenIDConfig, err = openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
NewCustomHTTPTransport(), xhttp.DrainBody) NewGatewayHTTPTransport(), xhttp.DrainBody)
if err != nil { if err != nil {
logger.LogIf(ctx, fmt.Errorf("Unable to initialize OpenID: %w", err)) logger.LogIf(ctx, fmt.Errorf("Unable to initialize OpenID: %w", err))
} }
opaCfg, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default], opaCfg, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
NewCustomHTTPTransport(), xhttp.DrainBody) NewGatewayHTTPTransport(), xhttp.DrainBody)
if err != nil { if err != nil {
logger.LogIf(ctx, fmt.Errorf("Unable to initialize OPA: %w", err)) logger.LogIf(ctx, fmt.Errorf("Unable to initialize OPA: %w", err))
} }
@ -412,22 +412,23 @@ func lookupConfigs(s config.Config) {
for _, l := range loggerCfg.HTTP { for _, l := range loggerCfg.HTTP {
if l.Enabled { if l.Enabled {
// Enable http logging // Enable http logging
logger.AddTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewCustomHTTPTransport())) logger.AddTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewGatewayHTTPTransport()))
} }
} }
for _, l := range loggerCfg.Audit { for _, l := range loggerCfg.Audit {
if l.Enabled { if l.Enabled {
// Enable http audit logging // Enable http audit logging
logger.AddAuditTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewCustomHTTPTransport())) logger.AddAuditTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewGatewayHTTPTransport()))
} }
} }
globalConfigTargetList, err = notify.GetNotificationTargets(s, GlobalServiceDoneCh, NewCustomHTTPTransport()) globalConfigTargetList, err = notify.GetNotificationTargets(s, GlobalServiceDoneCh, NewGatewayHTTPTransport())
if err != nil { if err != nil {
logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err)) logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
} }
globalEnvTargetList, err = notify.GetNotificationTargets(newServerConfig(), GlobalServiceDoneCh, NewCustomHTTPTransport())
globalEnvTargetList, err = notify.GetNotificationTargets(newServerConfig(), GlobalServiceDoneCh, NewGatewayHTTPTransport())
if err != nil { if err != nil {
logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err)) logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -150,7 +150,7 @@ func (g *Azure) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, erro
metrics := minio.NewMetrics() metrics := minio.NewMetrics()
t := &minio.MetricsTransport{ t := &minio.MetricsTransport{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
Metrics: metrics, Metrics: metrics,
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -102,7 +102,7 @@ func (g *B2) Name() string {
// talk to B2 remote backend. // talk to B2 remote backend.
func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) { func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
ctx := context.Background() ctx := context.Background()
client, err := b2.AuthorizeAccount(ctx, creds.AccessKey, creds.SecretKey, b2.Transport(minio.NewCustomHTTPTransport())) client, err := b2.AuthorizeAccount(ctx, creds.AccessKey, creds.SecretKey, b2.Transport(minio.NewGatewayHTTPTransport()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -111,7 +111,7 @@ func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
creds: creds, creds: creds,
b2Client: client, b2Client: client,
httpClient: &http.Client{ httpClient: &http.Client{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
}, },
ctx: ctx, ctx: ctx,
}, nil }, nil
@ -238,7 +238,7 @@ func (l *b2Objects) MakeBucketWithLocation(ctx context.Context, bucket, location
} }
func (l *b2Objects) reAuthorizeAccount(ctx context.Context) error { func (l *b2Objects) reAuthorizeAccount(ctx context.Context) error {
client, err := b2.AuthorizeAccount(l.ctx, l.creds.AccessKey, l.creds.SecretKey, b2.Transport(minio.NewCustomHTTPTransport())) client, err := b2.AuthorizeAccount(l.ctx, l.creds.AccessKey, l.creds.SecretKey, b2.Transport(minio.NewGatewayHTTPTransport()))
if err != nil { if err != nil {
return err return err
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -184,7 +184,7 @@ func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
metrics := minio.NewMetrics() metrics := minio.NewMetrics()
t := &minio.MetricsTransport{ t := &minio.MetricsTransport{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
Metrics: metrics, Metrics: metrics,
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -126,7 +126,7 @@ func (g *OSS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
return nil, err return nil, err
} }
client.HTTPClient = &http.Client{ client.HTTPClient = &http.Client{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
} }
return &ossObjects{ return &ossObjects{
Client: client, Client: client,

@ -152,7 +152,7 @@ var defaultAWSCredProviders = []credentials.Provider{
&credentials.FileAWSCredentials{}, &credentials.FileAWSCredentials{},
&credentials.IAM{ &credentials.IAM{
Client: &http.Client{ Client: &http.Client{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
}, },
}, },
&credentials.EnvMinio{}, &credentials.EnvMinio{},
@ -212,7 +212,7 @@ func (g *S3) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
metrics := minio.NewMetrics() metrics := minio.NewMetrics()
t := &minio.MetricsTransport{ t := &minio.MetricsTransport{
Transport: minio.NewCustomHTTPTransport(), Transport: minio.NewGatewayHTTPTransport(),
Metrics: metrics, Metrics: metrics,
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. * MinIO Cloud Storage, (C) 2015-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -721,7 +721,7 @@ func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
func setBucketForwardingHandler(h http.Handler) http.Handler { func setBucketForwardingHandler(h http.Handler) http.Handler {
fwd := handlers.NewForwarder(&handlers.Forwarder{ fwd := handlers.NewForwarder(&handlers.Forwarder{
PassHost: true, PassHost: true,
RoundTripper: NewCustomHTTPTransport(), RoundTripper: NewGatewayHTTPTransport(),
Logger: func(err error) { Logger: func(err error) {
logger.LogIf(context.Background(), err) logger.LogIf(context.Background(), err)
}, },

@ -784,6 +784,9 @@ func (sys *NotificationSys) RemoveNotification(bucketName string) {
// RemoveAllRemoteTargets - closes and removes all HTTP/PeerRPC client targets. // RemoveAllRemoteTargets - closes and removes all HTTP/PeerRPC client targets.
func (sys *NotificationSys) RemoveAllRemoteTargets() { func (sys *NotificationSys) RemoveAllRemoteTargets() {
sys.Lock()
defer sys.Unlock()
for _, targetMap := range sys.bucketRemoteTargetRulesMap { for _, targetMap := range sys.bucketRemoteTargetRulesMap {
for targetID := range targetMap { for targetID := range targetMap {
sys.targetList.Remove(targetID) sys.targetList.Remove(targetID)

@ -657,7 +657,7 @@ var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core,
if err != nil { if err != nil {
return nil, err return nil, err
} }
core.SetCustomTransport(NewCustomHTTPTransport()) core.SetCustomTransport(NewGatewayHTTPTransport())
return core, nil return core, nil
} }

@ -444,14 +444,16 @@ func (s *storageRESTServer) WalkHandler(w http.ResponseWriter, r *http.Request)
} }
leafFile := vars[storageRESTLeafFile] leafFile := vars[storageRESTLeafFile]
w.Header().Set(xhttp.ContentType, "text/event-stream")
encoder := gob.NewEncoder(w)
done := keepHTTPResponseAlive(w)
defer done()
fch, err := s.storage.Walk(volume, dirPath, markerPath, recursive, leafFile, readMetadata, r.Context().Done()) fch, err := s.storage.Walk(volume, dirPath, markerPath, recursive, leafFile, readMetadata, r.Context().Done())
if err != nil { if err != nil {
s.writeErrorResponse(w, err) logger.LogIf(r.Context(), err)
return return
} }
w.Header().Set(xhttp.ContentType, "text/event-stream")
encoder := gob.NewEncoder(w)
for fi := range fch { for fi := range fch {
encoder.Encode(&fi) encoder.Encode(&fi)
} }
@ -472,6 +474,7 @@ func (s *storageRESTServer) ListDirHandler(w http.ResponseWriter, r *http.Reques
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
} }
entries, err := s.storage.ListDir(volume, dirPath, count, leafFile) entries, err := s.storage.ListDir(volume, dirPath, count, leafFile)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
@ -521,20 +524,21 @@ func (s *storageRESTServer) DeleteFileBulkHandler(w http.ResponseWriter, r *http
return return
} }
dErrsResp := &DeleteFileBulkErrsResp{Errs: make([]error, len(filePaths))}
w.Header().Set(xhttp.ContentType, "text/event-stream") w.Header().Set(xhttp.ContentType, "text/event-stream")
encoder := gob.NewEncoder(w) encoder := gob.NewEncoder(w)
done := keepHTTPResponseAlive(w) done := keepHTTPResponseAlive(w)
errs, err := s.storage.DeleteFileBulk(volume, filePaths) errs, err := s.storage.DeleteFileBulk(volume, filePaths)
done() done()
if err != nil {
s.writeErrorResponse(w, err)
return
}
dErrsResp := &DeleteFileBulkErrsResp{Errs: make([]error, len(errs))} for idx := range filePaths {
for idx, err := range errs {
if err != nil { if err != nil {
dErrsResp.Errs[idx] = StorageErr(err.Error()) dErrsResp.Errs[idx] = StorageErr(err.Error())
} else {
if errs[idx] != nil {
dErrsResp.Errs[idx] = StorageErr(errs[idx].Error())
}
} }
} }
@ -567,20 +571,20 @@ func (s *storageRESTServer) DeletePrefixesHandler(w http.ResponseWriter, r *http
return return
} }
dErrsResp := &DeletePrefixesErrsResp{Errs: make([]error, len(prefixes))}
w.Header().Set(xhttp.ContentType, "text/event-stream") w.Header().Set(xhttp.ContentType, "text/event-stream")
encoder := gob.NewEncoder(w) encoder := gob.NewEncoder(w)
done := keepHTTPResponseAlive(w) done := keepHTTPResponseAlive(w)
errs, err := s.storage.DeletePrefixes(volume, prefixes) errs, err := s.storage.DeletePrefixes(volume, prefixes)
done() done()
if err != nil { for idx := range prefixes {
s.writeErrorResponse(w, err)
return
}
dErrsResp := &DeletePrefixesErrsResp{Errs: make([]error, len(errs))}
for idx, err := range errs {
if err != nil { if err != nil {
dErrsResp.Errs[idx] = StorageErr(err.Error()) dErrsResp.Errs[idx] = StorageErr(err.Error())
} else {
if errs[idx] != nil {
dErrsResp.Errs[idx] = StorageErr(errs[idx].Error())
}
} }
} }
encoder.Encode(dErrsResp) encoder.Encode(dErrsResp)

@ -151,10 +151,9 @@ const (
// (Acceptable values range from 1 to 10000 inclusive) // (Acceptable values range from 1 to 10000 inclusive)
globalMaxPartID = 10000 globalMaxPartID = 10000
// Default values used while communicating for // Default values used while communicating for internode communication.
// internode communication. defaultDialTimeout = 5 * time.Second
defaultDialTimeout = 15 * time.Second defaultDialKeepAlive = 15 * time.Second
defaultDialKeepAlive = 20 * time.Second
) )
// isMaxObjectSize - verify if max object size // isMaxObjectSize - verify if max object size
@ -452,7 +451,8 @@ func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout, dialKeepAlive ti
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
DialContext: newCustomDialContext(dialTimeout, dialKeepAlive), DialContext: newCustomDialContext(dialTimeout, dialKeepAlive),
MaxIdleConnsPerHost: 256, MaxIdleConnsPerHost: 256,
IdleConnTimeout: 60 * time.Second, IdleConnTimeout: time.Minute,
ResponseHeaderTimeout: 3 * time.Minute, // Set conservative timeouts for MinIO internode.
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 10 * time.Second, ExpectContinueTimeout: 10 * time.Second,
TLSClientConfig: tlsConfig, TLSClientConfig: tlsConfig,
@ -466,14 +466,17 @@ func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout, dialKeepAlive ti
} }
} }
// NewCustomHTTPTransport returns a new http configuration // NewGatewayHTTPTransport returns a new http configuration
// used while communicating with the cloud backends. // used while communicating with the cloud backends.
// This sets the value for MaxIdleConnsPerHost from 2 (go default) // This sets the value for MaxIdleConnsPerHost from 2 (go default)
// to 256. // to 256.
func NewCustomHTTPTransport() *http.Transport { func NewGatewayHTTPTransport() *http.Transport {
return newCustomHTTPTransport(&tls.Config{ tr := newCustomHTTPTransport(&tls.Config{
RootCAs: globalRootCAs, RootCAs: globalRootCAs,
}, defaultDialTimeout, defaultDialKeepAlive)() }, defaultDialTimeout, defaultDialKeepAlive)()
// Set aggressive timeouts for gateway
tr.ResponseHeaderTimeout = 30 * time.Second
return tr
} }
// Load the json (typically from disk file). // Load the json (typically from disk file).

@ -2050,7 +2050,7 @@ func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *
} }
clnt := &http.Client{ clnt := &http.Client{
Transport: NewCustomHTTPTransport(), Transport: NewGatewayHTTPTransport(),
} }
resp, err := clnt.Do(req) resp, err := clnt.Do(req)
@ -2059,6 +2059,7 @@ func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *
return toJSONError(ctx, err) return toJSONError(ctx, err)
} }
defer xhttp.DrainBody(resp.Body) defer xhttp.DrainBody(resp.Body)
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return toJSONError(ctx, errors.New(resp.Status)) return toJSONError(ctx, errors.New(resp.Status))
} }

@ -1 +1 @@
checks = ["all", "-ST1005", "-ST1000", "-SA4000", "-SA9004", "-SA1019", "-SA1008", "-U1000"] checks = ["all", "-ST1005", "-ST1000", "-SA4000", "-SA9004", "-SA1019", "-SA1008", "-U1000", "-ST1016"]

Loading…
Cancel
Save