5538: close any idle connections before a POST or DELETE request.
[arvados.git] / sdk / go / arvadosclient / arvadosclient.go
index af8bce4d4a7336cac0c66fb07292aaa203b4b0e6..1bc6f80f2cae1a2e17db60f2d5f844f542233f1e 100644 (file)
@@ -14,6 +14,7 @@ import (
        "os"
        "regexp"
        "strings"
+       "time"
 )
 
 type StringMatcher func(string) bool
@@ -25,6 +26,9 @@ var MissingArvadosApiHost = errors.New("Missing required environment variable AR
 var MissingArvadosApiToken = errors.New("Missing required environment variable ARVADOS_API_TOKEN")
 var ErrInvalidArgument = errors.New("Invalid argument")
 
+// Before a POST or DELERE request, close any connections that were idle for this long
+var MaxIdleConnectionDuration = 30 * time.Second
+
 // Indicates an error that was returned by the API server.
 type APIServerError struct {
        // Address of server returning error, of the form "host:port".
@@ -76,44 +80,25 @@ type ArvadosClient struct {
 
        // Discovery document
        DiscoveryDoc Dict
-}
 
-// APIConfig struct consists of:
-//    APIToken        string
-//    APIHost         string
-//    APIHostInsecure bool
-//    ExternalClient  bool
-type APIConfig struct {
-       APIToken        string
-       APIHost         string
-       APIHostInsecure bool
-       ExternalClient  bool
+       lastClosedIdlesAt time.Time
 }
 
-// Create a new ArvadosClient, initialized with standard Arvados environment variables
-// ARVADOS_API_HOST, ARVADOS_API_TOKEN, ARVADOS_API_HOST_INSECURE, ARVADOS_EXTERNAL_CLIENT.
+// Create a new ArvadosClient, initialized with standard Arvados environment
+// variables ARVADOS_API_HOST, ARVADOS_API_TOKEN, and (optionally)
+// ARVADOS_API_HOST_INSECURE.
 func MakeArvadosClient() (ac ArvadosClient, err error) {
-       var config APIConfig
-       config.APIToken = os.Getenv("ARVADOS_API_TOKEN")
-       config.APIHost = os.Getenv("ARVADOS_API_HOST")
-
        var matchTrue = regexp.MustCompile("^(?i:1|yes|true)$")
+       insecure := matchTrue.MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE"))
+       external := matchTrue.MatchString(os.Getenv("ARVADOS_EXTERNAL_CLIENT"))
 
-       config.APIHostInsecure = matchTrue.MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE"))
-       config.ExternalClient = matchTrue.MatchString(os.Getenv("ARVADOS_EXTERNAL_CLIENT"))
-
-       return New(config)
-}
-
-// Create a new ArvadosClient, using the given input parameters.
-func New(config APIConfig) (ac ArvadosClient, err error) {
        ac = ArvadosClient{
-               ApiServer:   config.APIHost,
-               ApiToken:    config.APIToken,
-               ApiInsecure: config.APIHostInsecure,
+               ApiServer:   os.Getenv("ARVADOS_API_HOST"),
+               ApiToken:    os.Getenv("ARVADOS_API_TOKEN"),
+               ApiInsecure: insecure,
                Client: &http.Client{Transport: &http.Transport{
-                       TLSClientConfig: &tls.Config{InsecureSkipVerify: config.APIHostInsecure}}},
-               External: config.ExternalClient}
+                       TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}}},
+               External: external}
 
        if ac.ApiServer == "" {
                return ac, MissingArvadosApiHost
@@ -122,6 +107,8 @@ func New(config APIConfig) (ac ArvadosClient, err error) {
                return ac, MissingArvadosApiToken
        }
 
+       ac.lastClosedIdlesAt = time.Now()
+
        return ac, err
 }
 
@@ -179,6 +166,14 @@ func (c ArvadosClient) CallRaw(method string, resourceType string, uuid string,
                req.Header.Add("X-External-Client", "1")
        }
 
+       // Before a POST or DELETE, close any idle connections
+       if method == "POST" || method == "DELETE" {
+               if time.Since(c.lastClosedIdlesAt) > MaxIdleConnectionDuration {
+                       c.lastClosedIdlesAt = time.Now()
+                       c.Client.Transport.(*http.Transport).CloseIdleConnections()
+               }
+       }
+
        // Make the request
        var resp *http.Response
        if resp, err = c.Client.Do(req); err != nil {