Repository: google/exposure-notifications-server
Pull request #1342 (2021-02-04 15:43:56)

Use custom http client and customizable timeout for jwks
**Release Note**

<!-- Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". -->

```release-note
Use a custom http client with a separate request timeout for the jwks service. Operators can customize the timeout by setting `REQUEST_TIMEOUT` on the jwks service. The default value is 5 seconds.
```

/assign @whaught 

google-oss-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/google/exposure-notifications-server/pull/1342#" title="Author self-approved">sethvargo</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=google%2Fexposure-notifications-server).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/google/exposure-notifications-server/blob/main/OWNERS)~~ [sethvargo]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
sethvargo: This will need a rebase after 1341
google-oss-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/google/exposure-notifications-server/pull/1342#" title="Author self-approved">sethvargo</a>*, *<a href="https://github.com/google/exposure-notifications-server/pull/1342#pullrequestreview-583544093" title="Approved">whaught</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=google%2Fexposure-notifications-server).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/google/exposure-notifications-server/blob/main/OWNERS)~~ [sethvargo,whaught]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
google-oss-robot: New changes are detected. LGTM label has been removed.
google-oss-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/google/exposure-notifications-server/pull/1342#" title="Author self-approved">sethvargo</a>*, *<a href="https://github.com/google/exposure-notifications-server/pull/1342#pullrequestreview-583546658" title="Approved">whaught</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=google%2Fexposure-notifications-server).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/google/exposure-notifications-server/blob/main/OWNERS)~~ [sethvargo,whaught]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/internal/jwks/config.go b/internal/jwks/config.go
index b55fda9..cd31217 100644
--- a/internal/jwks/config.go
+++ b/internal/jwks/config.go
@@ -31,6 +31,10 @@ type Config struct {
 
 	Port string `env:"PORT, default=8080"`
 
+	// RequestTimeout is the per-request amount of time to wait for a response
+	// before timing out.
+	RequestTimeout time.Duration `env:"REQUEST_TIMEOUT, default=5s"`
+
 	KeyCleanupTTL time.Duration `env:"HEALTH_AUTHORITY_KEY_CLEANUP_TTL, default=720h"` // 30 days
 }
 
diff --git a/internal/jwks/jwks.go b/internal/jwks/jwks.go
index 23322ad..82a474d 100644
--- a/internal/jwks/jwks.go
+++ b/internal/jwks/jwks.go
@@ -42,17 +42,23 @@ import (
 // URI.
 type Manager struct {
 	db         *database.DB
+	client     *http.Client
 	cleanupTTL time.Duration
 }
 
 // NewManager creates a new Manager.
-func NewManager(db *database.DB, cleanupTTL time.Duration) (*Manager, error) {
+func NewManager(db *database.DB, cleanupTTL, requestTimeout time.Duration) (*Manager, error) {
 	if cleanupTTL < 0 {
 		cleanupTTL *= -1
 	}
 
+	client := &http.Client{
+		Timeout: requestTimeout,
+	}
+
 	return &Manager{
 		db:         db,
+		client:     client,
 		cleanupTTL: cleanupTTL,
 	}, nil
 }
@@ -74,7 +80,7 @@ func (mgr *Manager) getKeys(ctx context.Context, ha *model.HealthAuthority) ([]b
 		return nil, fmt.Errorf("creating connection: %w", err)
 	}
 
-	resp, err := http.DefaultClient.Do(req)
+	resp, err := mgr.client.Do(req)
 	if err != nil {
 		return nil, fmt.Errorf("reading connection: %w", err)
 	}
diff --git a/internal/jwks/server.go b/internal/jwks/server.go
index 462bb47..592798c 100644
--- a/internal/jwks/server.go
+++ b/internal/jwks/server.go
@@ -39,7 +39,7 @@ func NewServer(cfg *Config, env *serverenv.ServerEnv) (*Server, error) {
 		return nil, fmt.Errorf("missing database in server env")
 	}
 
-	manager, err := NewManager(env.Database(), cfg.KeyCleanupTTL)
+	manager, err := NewManager(env.Database(), cfg.KeyCleanupTTL, cfg.RequestTimeout)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create manager: %w", err)
 	}


Test patch:
diff --git a/internal/jwks/jwks_test.go b/internal/jwks/jwks_test.go
index bcbea21..8194e77 100644
--- a/internal/jwks/jwks_test.go
+++ b/internal/jwks/jwks_test.go
@@ -139,7 +139,7 @@ func TestUpdateHA(t *testing.T) {
 			// Set up the test.
 			ctx := context.Background()
 			testDB, _ := testDatabaseInstance.NewDatabase(t)
-			mgr, err := NewManager(testDB, time.Minute)
+			mgr, err := NewManager(testDB, time.Minute, 5*time.Second)
 			if err != nil {
 				t.Fatalf("[%d] unexpected error: %v", i, err)
 			}