Skip to content

Commit fd28a40

Browse files
committed
fix(ari): retry on alreadyReplaced error
1 parent 2bc147f commit fd28a40

File tree

4 files changed

+30
-3
lines changed

4 files changed

+30
-3
lines changed

acme/api/internal/sender/sender.go

+4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ func checkError(req *http.Request, resp *http.Response) error {
142142
return &acme.NonceError{ProblemDetails: errorDetails}
143143
}
144144

145+
if errorDetails.HTTPStatus == http.StatusConflict && errorDetails.Type == acme.AlreadyReplacedErr {
146+
return &acme.AlreadyReplacedError{ProblemDetails: errorDetails}
147+
}
148+
145149
return errorDetails
146150
}
147151
return nil

acme/api/order.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,20 @@ func (o *OrderService) NewWithOptions(domains []string, opts *OrderOptions) (acm
6969
var order acme.Order
7070
resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order)
7171
if err != nil {
72-
return acme.ExtendedOrder{}, err
72+
are := &acme.AlreadyReplacedError{}
73+
if !errors.As(err, &are) {
74+
return acme.ExtendedOrder{}, err
75+
}
76+
77+
// If the Server rejects the request because the identified certificate has already been marked as replaced,
78+
// it MUST return an HTTP 409 (Conflict) with a problem document of type "alreadyReplaced" (see Section 7.4).
79+
// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-08#section-5
80+
orderReq.Replaces = ""
81+
82+
resp, err = o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order)
83+
if err != nil {
84+
return acme.ExtendedOrder{}, err
85+
}
7386
}
7487

7588
return acme.ExtendedOrder{

acme/api/order_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ func TestOrderService_NewWithOptions(t *testing.T) {
4646
Status: acme.StatusValid,
4747
Expires: order.Expires,
4848
Identifiers: order.Identifiers,
49+
Profile: order.Profile,
4950
NotBefore: order.NotBefore,
5051
NotAfter: order.NotAfter,
5152
Error: order.Error,
5253
Authorizations: order.Authorizations,
5354
Finalize: order.Finalize,
5455
Certificate: order.Certificate,
56+
Replaces: order.Replaces,
5557
})
5658
if err != nil {
5759
http.Error(w, err.Error(), http.StatusInternalServerError)

acme/errors.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66

77
// Errors types.
88
const (
9-
errNS = "urn:ietf:params:acme:error:"
10-
BadNonceErr = errNS + "badNonce"
9+
errNS = "urn:ietf:params:acme:error:"
10+
BadNonceErr = errNS + "badNonce"
11+
AlreadyReplacedErr = errNS + "alreadyReplaced"
1112
)
1213

1314
// ProblemDetails the problem details object.
@@ -56,3 +57,10 @@ type SubProblem struct {
5657
type NonceError struct {
5758
*ProblemDetails
5859
}
60+
61+
// AlreadyReplacedError represents the error which is returned
62+
// If the Server rejects the request because the identified certificate has already been marked as replaced.
63+
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-08#section-5
64+
type AlreadyReplacedError struct {
65+
*ProblemDetails
66+
}

0 commit comments

Comments
 (0)