Browse Source

Move fetching new nonces from nonce stack into the client

master
isaac 3 years ago
parent
commit
84e95c3c43
  1. 48
      acme.go
  2. 36
      nonce.go
  3. 29
      nonce_test.go

48
acme.go

@ -13,6 +13,7 @@ import (
"bytes"
"crypto"
"errors"
)
const (
@ -34,16 +35,14 @@ func NewClient(directoryURL string, options ...OptionFunc) (Client, error) {
// can be overridden via OptionFunc eg: acme.NewClient(url, WithHTTPTimeout(10 * time.Second))
httpClient.Timeout = 60 * time.Second
ns := &nonceStack{
client: httpClient,
}
acmeClient := Client{
httpClient: httpClient,
nonces: ns,
nonces: &nonceStack{},
retryCount: 5,
}
acmeClient.dir.URL = directoryURL
for _, opt := range options {
if err := opt(&acmeClient); err != nil {
return acmeClient, fmt.Errorf("acme: error setting option: %v", err)
@ -54,9 +53,6 @@ func NewClient(directoryURL string, options ...OptionFunc) (Client, error) {
return acmeClient, err
}
acmeClient.dir.URL = directoryURL
ns.newNonceURL = acmeClient.dir.NewNonce
return acmeClient, nil
}
@ -80,7 +76,7 @@ func (c Client) getPollingDurations() (time.Duration, time.Duration) {
// Helper function to have a central point for performing http requests.
// Stores any returned nonces in the stack.
func (c Client) do(req *http.Request) (*http.Response, error) {
func (c Client) do(req *http.Request, addNonce bool) (*http.Response, error) {
// More details: https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-6.1
// identifier for this client, as well as the default go user agent
if c.userAgentSuffix != "" {
@ -98,7 +94,9 @@ func (c Client) do(req *http.Request) (*http.Response, error) {
return resp, err
}
c.nonces.push(resp.Header.Get("Replay-Nonce"))
if addNonce {
c.nonces.push(resp.Header.Get("Replay-Nonce"))
}
return resp, nil
}
@ -110,7 +108,7 @@ func (c Client) getRaw(url string, expectedStatus ...int) (*http.Response, []byt
return nil, nil, fmt.Errorf("acme: error creating request: %v", err)
}
resp, err := c.do(req)
resp, err := c.do(req, true)
if err != nil {
return resp, nil, fmt.Errorf("acme: error fetching response: %v", err)
}
@ -144,10 +142,34 @@ func (c Client) get(url string, out interface{}, expectedStatus ...int) (*http.R
return resp, nil
}
func (c Client) nonce() (string, error) {
nonce := c.nonces.pop()
if nonce != "" {
return nonce, nil
}
if c.dir.NewNonce == "" {
return "", errors.New("acme: no new nonce url")
}
req, err := http.NewRequest("HEAD", c.dir.NewNonce, nil)
if err != nil {
return "", fmt.Errorf("acme: error creating new nonce request: %v", err)
}
resp, err := c.do(req, false)
if err != nil {
return "", fmt.Errorf("acme: error fetching new nonce: %v", err)
}
nonce = resp.Header.Get("Replay-Nonce")
return nonce, nil
}
// Helper function to perform an http post request and read the body.
// Will attempt to retry if error is badNonce
func (c Client) postRaw(retryCount int, requestURL, keyID string, privateKey crypto.Signer, payload interface{}, out interface{}, expectedStatus []int) (*http.Response, []byte, error) {
nonce, err := c.nonces.Nonce()
nonce, err := c.nonce()
if err != nil {
return nil, nil, err
}
@ -163,7 +185,7 @@ func (c Client) postRaw(retryCount int, requestURL, keyID string, privateKey cry
}
req.Header.Set("Content-Type", "application/jose+json")
resp, err := c.do(req)
resp, err := c.do(req, true)
if err != nil {
return resp, nil, fmt.Errorf("acme: error sending request: %v", err)
}

36
nonce.go

@ -1,9 +1,6 @@
package acme
import (
"errors"
"fmt"
"net/http"
"sync"
)
@ -11,9 +8,6 @@ import (
type nonceStack struct {
lock sync.Mutex
stack []string
client *http.Client
newNonceURL string
}
// Pushes a nonce to the stack.
@ -49,33 +43,3 @@ func (ns *nonceStack) pop() string {
return v
}
// Used to insert a nonce field into a jws header.
func (ns *nonceStack) Nonce() (string, error) {
nonce := ns.pop()
if nonce != "" {
return nonce, nil
}
if ns.newNonceURL == "" {
return "", errors.New("acme: no newNonce url")
}
req, err := http.NewRequest("HEAD", ns.newNonceURL, nil)
if err != nil {
return "", fmt.Errorf("acme: error creating newNonce request: %v", err)
}
req.Header.Set("User-Agent", userAgentString)
resp, err := ns.client.Head(ns.newNonceURL)
if err != nil {
return "", fmt.Errorf("acme: error fetching new nonce: %v", err)
}
replaceNonce := resp.Header.Get("Replay-Nonce")
if replaceNonce == "" {
return "", errors.New("acme: no nonce sent")
}
return replaceNonce, nil
}

29
nonce_test.go

@ -1,45 +1,26 @@
package acme
import (
"net/http"
"testing"
)
func TestNonceStack_Nonce(t *testing.T) {
ns := nonceStack{
client: http.DefaultClient,
}
func TestNonceStack(t *testing.T) {
ns := nonceStack{}
ns.push("test")
if len(ns.stack) != 1 {
t.Fatalf("expected stack size of 1, got: %d", len(ns.stack))
}
nonce, err := ns.Nonce()
if err != nil {
t.Fatalf("unexpected error popping nonce from stack: %v", err)
}
nonce := ns.pop()
if nonce != "test" {
t.Fatalf("bad nonce returned from stack, expected %q got %q", "test", nonce)
}
if _, err = ns.Nonce(); err == nil {
t.Fatal("expected error, got none")
}
ns.newNonceURL = "http://google.com/"
if _, err = ns.Nonce(); err == nil {
t.Fatal("expected error, got none")
if nonce := ns.pop(); nonce != "" {
t.Fatalf("expected no nonce, got: %v", nonce)
}
ns.newNonceURL = testClient.Directory().NewNonce
nonce, err = ns.Nonce()
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
if nonce == "" {
t.Fatal("no nonce returned")
}
if len(ns.stack) != 0 {
t.Fatal("expected empty stack")
}

Loading…
Cancel
Save