@@ -46,6 +46,10 @@ const (
46
46
// Max number of collected nonces kept in memory.
47
47
// Expect usual peak of 1 or 2.
48
48
maxNonces = 100
49
+
50
+ // User-Agent, bump the version each time a change is made to the
51
+ // handling of API requests.
52
+ userAgent = "go-acme/2"
49
53
)
50
54
51
55
// Client is an ACME client.
@@ -73,6 +77,11 @@ type Client struct {
73
77
// will have no effect.
74
78
DirectoryURL string
75
79
80
+ // UserAgent is an optional string that identifies this client and
81
+ // version to the ACME server. It should be set to something like
82
+ // "myclient/1.2.3".
83
+ UserAgent string
84
+
76
85
noncesMu sync.Mutex
77
86
nonces map [string ]struct {} // nonces collected from previous responses
78
87
@@ -274,6 +283,13 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
274
283
sleep := timeSleeper (ctx )
275
284
for {
276
285
o , err := c .GetOrder (ctx , url )
286
+ if e , ok := err .(* Error ); ok && e .StatusCode >= 500 && e .StatusCode <= 599 {
287
+ // retriable 5xx error
288
+ if err := sleep (retryAfter (e .Header .Get ("Retry-After" ))); err != nil {
289
+ return nil , err
290
+ }
291
+ continue
292
+ }
277
293
if err != nil {
278
294
return nil , err
279
295
}
@@ -392,32 +408,36 @@ func (c *Client) DeactivateAuthorization(ctx context.Context, url string) error
392
408
return nil
393
409
}
394
410
395
- // WaitAuthorization retrieves authorization details. If the authorization is not in
396
- // a final state (StatusValid/StatusInvalid), it retries the request until the authorization
397
- // is final, ctx is cancelled by the caller, an error response is received, or the ACME CA
398
- // responded with a 4xx error.
411
+ // WaitAuthorization polls an authorization at the given URL
412
+ // until it is in one of the final states, StatusValid or StatusInvalid,
413
+ // the ACME CA responded with a 4xx error code, or the context is done.
399
414
//
400
415
// It returns a non-nil Authorization only if its Status is StatusValid.
401
416
// In all other cases WaitAuthorization returns an error.
402
- // If the Status is StatusInvalid or StatusDeactivated, the returned error will be of type AuthorizationError.
417
+ // If the Status is StatusInvalid, StatusDeactivated, or StatusRevoked the
418
+ // returned error will be of type AuthorizationError.
403
419
func (c * Client ) WaitAuthorization (ctx context.Context , url string ) (* Authorization , error ) {
404
420
sleep := sleeper (ctx )
405
421
for {
406
422
res , err := c .get (ctx , url )
407
423
if err != nil {
408
424
return nil , err
409
425
}
410
- if res .StatusCode != http .StatusOK {
411
- err = responseError (res )
412
- res .Body .Close ()
413
- return nil , err
414
- }
415
426
if res .StatusCode >= 400 && res .StatusCode <= 499 {
416
427
// Non-retriable error. For instance, Let's Encrypt may return 404 Not Found
417
428
// when requesting an expired authorization.
418
429
defer res .Body .Close ()
419
430
return nil , responseError (res )
420
431
}
432
+
433
+ retry := res .Header .Get ("Retry-After" )
434
+ if res .StatusCode != http .StatusOK && res .StatusCode != http .StatusAccepted {
435
+ res .Body .Close ()
436
+ if err := sleep (retry ); err != nil {
437
+ return nil , err
438
+ }
439
+ continue
440
+ }
421
441
var raw wireAuthz
422
442
err = json .NewDecoder (res .Body ).Decode (& raw )
423
443
res .Body .Close ()
@@ -427,13 +447,13 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
427
447
switch raw .Status {
428
448
case StatusValid :
429
449
return raw .authorization (url ), nil
430
- case StatusInvalid , StatusDeactivated :
450
+ case StatusInvalid , StatusDeactivated , StatusRevoked :
431
451
return nil , AuthorizationError {raw .authorization (url )}
432
452
case StatusPending , StatusProcessing : // fall through to sleep
433
453
default :
434
454
return nil , fmt .Errorf ("acme: unknown authorization status %q" , raw .Status )
435
455
}
436
- if err := sleep (res . Header . Get ( "Retry-After" ) ); err != nil {
456
+ if err := sleep (retry ); err != nil {
437
457
return nil , err
438
458
}
439
459
}
@@ -723,30 +743,43 @@ func (c *Client) httpClient() *http.Client {
723
743
}
724
744
725
745
func (c * Client ) get (ctx context.Context , urlStr string ) (* http.Response , error ) {
726
- req , err := http . NewRequest ("GET" , urlStr , nil )
746
+ req , err := c . newRequest ("GET" , urlStr , nil )
727
747
if err != nil {
728
748
return nil , err
729
749
}
730
750
return c .do (ctx , req )
731
751
}
732
752
733
753
func (c * Client ) head (ctx context.Context , urlStr string ) (* http.Response , error ) {
734
- req , err := http . NewRequest ("HEAD" , urlStr , nil )
754
+ req , err := c . newRequest ("HEAD" , urlStr , nil )
735
755
if err != nil {
736
756
return nil , err
737
757
}
738
758
return c .do (ctx , req )
739
759
}
740
760
741
761
func (c * Client ) post (ctx context.Context , urlStr , contentType string , body io.Reader ) (* http.Response , error ) {
742
- req , err := http . NewRequest ("POST" , urlStr , body )
762
+ req , err := c . newRequest ("POST" , urlStr , body )
743
763
if err != nil {
744
764
return nil , err
745
765
}
746
766
req .Header .Set ("Content-Type" , contentType )
747
767
return c .do (ctx , req )
748
768
}
749
769
770
+ func (c * Client ) newRequest (method , url string , body io.Reader ) (* http.Request , error ) {
771
+ req , err := http .NewRequest (method , url , body )
772
+ if err != nil {
773
+ return nil , err
774
+ }
775
+ ua := userAgent
776
+ if c .UserAgent != "" {
777
+ ua += " " + c .UserAgent
778
+ }
779
+ req .Header .Set ("User-Agent" , ua )
780
+ return req , nil
781
+ }
782
+
750
783
func (c * Client ) do (ctx context.Context , req * http.Request ) (* http.Response , error ) {
751
784
res , err := c .httpClient ().Do (req .WithContext (ctx ))
752
785
if err != nil {
0 commit comments