aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/crypto/acme/http_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/crypto/acme/http_test.go')
-rw-r--r--vendor/golang.org/x/crypto/acme/http_test.go209
1 files changed, 209 insertions, 0 deletions
diff --git a/vendor/golang.org/x/crypto/acme/http_test.go b/vendor/golang.org/x/crypto/acme/http_test.go
new file mode 100644
index 0000000..15e401b
--- /dev/null
+++ b/vendor/golang.org/x/crypto/acme/http_test.go
@@ -0,0 +1,209 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package acme
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestDefaultBackoff(t *testing.T) {
+ tt := []struct {
+ nretry int
+ retryAfter string // Retry-After header
+ out time.Duration // expected min; max = min + jitter
+ }{
+ {-1, "", time.Second}, // verify the lower bound is 1
+ {0, "", time.Second}, // verify the lower bound is 1
+ {100, "", 10 * time.Second}, // verify the ceiling
+ {1, "3600", time.Hour}, // verify the header value is used
+ {1, "", 1 * time.Second},
+ {2, "", 2 * time.Second},
+ {3, "", 4 * time.Second},
+ {4, "", 8 * time.Second},
+ }
+ for i, test := range tt {
+ r := httptest.NewRequest("GET", "/", nil)
+ resp := &http.Response{Header: http.Header{}}
+ if test.retryAfter != "" {
+ resp.Header.Set("Retry-After", test.retryAfter)
+ }
+ d := defaultBackoff(test.nretry, r, resp)
+ max := test.out + time.Second // + max jitter
+ if d < test.out || max < d {
+ t.Errorf("%d: defaultBackoff(%v) = %v; want between %v and %v", i, test.nretry, d, test.out, max)
+ }
+ }
+}
+
+func TestErrorResponse(t *testing.T) {
+ s := `{
+ "status": 400,
+ "type": "urn:acme:error:xxx",
+ "detail": "text"
+ }`
+ res := &http.Response{
+ StatusCode: 400,
+ Status: "400 Bad Request",
+ Body: ioutil.NopCloser(strings.NewReader(s)),
+ Header: http.Header{"X-Foo": {"bar"}},
+ }
+ err := responseError(res)
+ v, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("err = %+v (%T); want *Error type", err, err)
+ }
+ if v.StatusCode != 400 {
+ t.Errorf("v.StatusCode = %v; want 400", v.StatusCode)
+ }
+ if v.ProblemType != "urn:acme:error:xxx" {
+ t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType)
+ }
+ if v.Detail != "text" {
+ t.Errorf("v.Detail = %q; want text", v.Detail)
+ }
+ if !reflect.DeepEqual(v.Header, res.Header) {
+ t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header)
+ }
+}
+
+func TestPostWithRetries(t *testing.T) {
+ var count int
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ count++
+ w.Header().Set("Replay-Nonce", fmt.Sprintf("nonce%d", count))
+ if r.Method == "HEAD" {
+ // We expect the client to do 2 head requests to fetch
+ // nonces, one to start and another after getting badNonce
+ return
+ }
+
+ head, err := decodeJWSHead(r)
+ switch {
+ case err != nil:
+ t.Errorf("decodeJWSHead: %v", err)
+ case head.Nonce == "":
+ t.Error("head.Nonce is empty")
+ case head.Nonce == "nonce1":
+ // Return a badNonce error to force the call to retry.
+ w.Header().Set("Retry-After", "0")
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte(`{"type":"urn:ietf:params:acme:error:badNonce"}`))
+ return
+ }
+ // Make client.Authorize happy; we're not testing its result.
+ w.WriteHeader(http.StatusCreated)
+ w.Write([]byte(`{"status":"valid"}`))
+ }))
+ defer ts.Close()
+
+ client := &Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
+ // This call will fail with badNonce, causing a retry
+ if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
+ t.Errorf("client.Authorize 1: %v", err)
+ }
+ if count != 4 {
+ t.Errorf("total requests count: %d; want 4", count)
+ }
+}
+
+func TestRetryErrorType(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Replay-Nonce", "nonce")
+ w.WriteHeader(http.StatusTooManyRequests)
+ w.Write([]byte(`{"type":"rateLimited"}`))
+ }))
+ defer ts.Close()
+
+ client := &Client{
+ Key: testKey,
+ RetryBackoff: func(n int, r *http.Request, res *http.Response) time.Duration {
+ // Do no retries.
+ return 0
+ },
+ dir: &Directory{AuthzURL: ts.URL},
+ }
+
+ t.Run("post", func(t *testing.T) {
+ testRetryErrorType(t, func() error {
+ _, err := client.Authorize(context.Background(), "example.com")
+ return err
+ })
+ })
+ t.Run("get", func(t *testing.T) {
+ testRetryErrorType(t, func() error {
+ _, err := client.GetAuthorization(context.Background(), ts.URL)
+ return err
+ })
+ })
+}
+
+func testRetryErrorType(t *testing.T, callClient func() error) {
+ t.Helper()
+ err := callClient()
+ if err == nil {
+ t.Fatal("client.Authorize returned nil error")
+ }
+ acmeErr, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("err is %v (%T); want *Error", err, err)
+ }
+ if acmeErr.StatusCode != http.StatusTooManyRequests {
+ t.Errorf("acmeErr.StatusCode = %d; want %d", acmeErr.StatusCode, http.StatusTooManyRequests)
+ }
+ if acmeErr.ProblemType != "rateLimited" {
+ t.Errorf("acmeErr.ProblemType = %q; want 'rateLimited'", acmeErr.ProblemType)
+ }
+}
+
+func TestRetryBackoffArgs(t *testing.T) {
+ const resCode = http.StatusInternalServerError
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Replay-Nonce", "test-nonce")
+ w.WriteHeader(resCode)
+ }))
+ defer ts.Close()
+
+ // Canceled in backoff.
+ ctx, cancel := context.WithCancel(context.Background())
+
+ var nretry int
+ backoff := func(n int, r *http.Request, res *http.Response) time.Duration {
+ nretry++
+ if n != nretry {
+ t.Errorf("n = %d; want %d", n, nretry)
+ }
+ if nretry == 3 {
+ cancel()
+ }
+
+ if r == nil {
+ t.Error("r is nil")
+ }
+ if res.StatusCode != resCode {
+ t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, resCode)
+ }
+ return time.Millisecond
+ }
+
+ client := &Client{
+ Key: testKey,
+ RetryBackoff: backoff,
+ dir: &Directory{AuthzURL: ts.URL},
+ }
+ if _, err := client.Authorize(ctx, "example.com"); err == nil {
+ t.Error("err is nil")
+ }
+ if nretry != 3 {
+ t.Errorf("nretry = %d; want 3", nretry)
+ }
+}