@@ -9,17 +9,18 @@ use core::time::Duration;
9
9
use std:: collections:: VecDeque ;
10
10
use std:: string:: { String , ToString } ;
11
11
12
- use anyhow:: { anyhow, Result } ;
13
12
use bytes:: Bytes ;
13
+ use error:: { NewAccountError , NewCertificateError , RequestError } ;
14
14
use http:: Uri ;
15
15
use ngx:: allocator:: { Allocator , Box } ;
16
16
use ngx:: async_:: sleep;
17
17
use ngx:: collections:: Vec ;
18
18
use ngx:: ngx_log_debug;
19
19
use openssl:: pkey:: { PKey , PKeyRef , Private } ;
20
20
use openssl:: x509:: { self , extension as x509_ext, X509Req } ;
21
+ use types:: ProblemCategory ;
21
22
22
- use self :: account_key:: AccountKey ;
23
+ use self :: account_key:: { AccountKey , AccountKeyError } ;
23
24
use self :: types:: { AuthorizationStatus , ChallengeKind , ChallengeStatus , OrderStatus } ;
24
25
use crate :: conf:: identifier:: Identifier ;
25
26
use crate :: conf:: issuer:: Issuer ;
@@ -28,6 +29,7 @@ use crate::net::http::HttpClient;
28
29
use crate :: time:: Time ;
29
30
30
31
pub mod account_key;
32
+ pub mod error;
31
33
pub mod solvers;
32
34
pub mod types;
33
35
@@ -97,8 +99,13 @@ fn try_get_header<K: http::header::AsHeaderName>(
97
99
impl < ' a , Http > AcmeClient < ' a , Http >
98
100
where
99
101
Http : HttpClient ,
102
+ RequestError : From < <Http as HttpClient >:: Error > ,
100
103
{
101
- pub fn new ( http : Http , issuer : & ' a Issuer , log : NonNull < nginx_sys:: ngx_log_t > ) -> Result < Self > {
104
+ pub fn new (
105
+ http : Http ,
106
+ issuer : & ' a Issuer ,
107
+ log : NonNull < nginx_sys:: ngx_log_t > ,
108
+ ) -> Result < Self , AccountKeyError > {
102
109
let key = AccountKey :: try_from (
103
110
issuer
104
111
. pkey
@@ -137,21 +144,21 @@ where
137
144
self . solvers . iter ( ) . any ( |s| s. supports ( kind) )
138
145
}
139
146
140
- async fn get_directory ( & self ) -> Result < types:: Directory > {
147
+ async fn get_directory ( & self ) -> Result < types:: Directory , RequestError > {
141
148
let res = self . get ( & self . issuer . uri ) . await ?;
142
- let directory = serde_json :: from_slice ( res. body ( ) ) ?;
149
+ let directory = deserialize_body ( res. body ( ) ) ?;
143
150
144
151
Ok ( directory)
145
152
}
146
153
147
- async fn get_nonce ( & self ) -> Result < String > {
154
+ async fn get_nonce ( & self ) -> Result < String , RequestError > {
148
155
let res = self . get ( & self . directory . new_nonce ) . await ?;
149
156
try_get_header ( res. headers ( ) , & REPLAY_NONCE )
150
- . ok_or ( anyhow ! ( "no nonce in response headers" ) )
157
+ . ok_or ( RequestError :: Nonce )
151
158
. map ( String :: from)
152
159
}
153
160
154
- pub async fn get ( & self , url : & Uri ) -> Result < http:: Response < Bytes > > {
161
+ pub async fn get ( & self , url : & Uri ) -> Result < http:: Response < Bytes > , RequestError > {
155
162
let req = http:: Request :: builder ( )
156
163
. uri ( url)
157
164
. method ( http:: Method :: GET )
@@ -164,7 +171,7 @@ where
164
171
& self ,
165
172
url : & Uri ,
166
173
payload : P ,
167
- ) -> Result < http:: Response < Bytes > > {
174
+ ) -> Result < http:: Response < Bytes > , RequestError > {
168
175
let mut nonce = if let Some ( nonce) = self . nonce . get ( ) {
169
176
nonce
170
177
} else {
@@ -215,10 +222,10 @@ where
215
222
// 8555.6.5, when retrying in response to a "badNonce" error, the client MUST use
216
223
// the nonce provided in the error response.
217
224
nonce = try_get_header ( res. headers ( ) , & REPLAY_NONCE )
218
- . ok_or ( anyhow ! ( "no nonce in response" ) ) ?
225
+ . ok_or ( RequestError :: Nonce ) ?
219
226
. to_string ( ) ;
220
227
221
- let err: types:: Problem = serde_json :: from_slice ( res. body ( ) ) ?;
228
+ let err: types:: Problem = deserialize_body ( res. body ( ) ) ?;
222
229
223
230
let retriable = matches ! (
224
231
err. kind,
@@ -239,20 +246,23 @@ where
239
246
Ok ( res)
240
247
}
241
248
242
- pub async fn new_account ( & mut self ) -> Result < types:: Account > {
243
- self . directory = self . get_directory ( ) . await ?;
249
+ pub async fn new_account ( & mut self ) -> Result < ( ) , NewAccountError > {
250
+ self . directory = self
251
+ . get_directory ( )
252
+ . await
253
+ . map_err ( NewAccountError :: Directory ) ?;
244
254
245
255
if self . directory . meta . external_account_required == Some ( true )
246
256
&& self . issuer . eab_key . is_none ( )
247
257
{
248
- return Err ( anyhow ! ( "external account key required" ) ) ;
258
+ return Err ( NewAccountError :: ExternalAccount ) ;
249
259
}
250
260
251
261
let external_account_binding = self
252
262
. issuer
253
263
. eab_key
254
264
. as_ref ( )
255
- . map ( |x| -> Result < _ > {
265
+ . map ( |x| -> Result < _ , RequestError > {
256
266
let key = crate :: jws:: ShaWithHmacKey :: new ( & x. key , 256 ) ;
257
267
let payload = serde_json:: to_vec ( & self . key ) ?;
258
268
let message = crate :: jws:: sign_jws (
@@ -273,19 +283,16 @@ where
273
283
274
284
..Default :: default ( )
275
285
} ;
276
- let payload = serde_json:: to_string ( & payload) ?;
286
+ let payload = serde_json:: to_string ( & payload) . map_err ( RequestError :: RequestFormat ) ?;
277
287
278
288
let res = self . post ( & self . directory . new_account , payload) . await ?;
279
289
280
- let key_id = res
281
- . headers ( )
282
- . get ( "location" )
283
- . ok_or ( anyhow ! ( "account URL unavailable" ) ) ?
284
- . to_str ( ) ?
285
- . to_string ( ) ;
286
- self . account = Some ( key_id) ;
290
+ let key_id: & str =
291
+ try_get_header ( res. headers ( ) , http:: header:: LOCATION ) . ok_or ( NewAccountError :: Url ) ?;
292
+
293
+ self . account = Some ( key_id. to_string ( ) ) ;
287
294
288
- Ok ( serde_json :: from_slice ( res . body ( ) ) ? )
295
+ Ok ( ( ) )
289
296
}
290
297
291
298
pub fn is_ready ( & self ) -> bool {
@@ -295,7 +302,7 @@ where
295
302
pub async fn new_certificate < A > (
296
303
& self ,
297
304
req : & CertificateOrder < & str , A > ,
298
- ) -> Result < NewCertificateOutput >
305
+ ) -> Result < NewCertificateOutput , NewCertificateError >
299
306
where
300
307
A : Allocator ,
301
308
{
@@ -313,30 +320,27 @@ where
313
320
not_after : None ,
314
321
} ;
315
322
316
- let payload = serde_json:: to_string ( & payload) ?;
323
+ let payload = serde_json:: to_string ( & payload) . map_err ( RequestError :: RequestFormat ) ?;
317
324
318
325
let res = self . post ( & self . directory . new_order , payload) . await ?;
319
326
320
- let order_url = res
321
- . headers ( )
322
- . get ( "location" )
323
- . and_then ( |x| x. to_str ( ) . ok ( ) )
324
- . ok_or ( anyhow ! ( "no order URL" ) ) ?;
327
+ let order_url = try_get_header ( res. headers ( ) , http:: header:: LOCATION )
328
+ . and_then ( |x| Uri :: try_from ( x) . ok ( ) )
329
+ . ok_or ( NewCertificateError :: OrderUrl ) ?;
325
330
326
- let order_url = Uri :: try_from ( order_url) ?;
327
- let order: types:: Order = serde_json:: from_slice ( res. body ( ) ) ?;
331
+ let order: types:: Order = deserialize_body ( res. body ( ) ) ?;
328
332
329
333
let mut authorizations: Vec < ( http:: Uri , types:: Authorization ) > = Vec :: new ( ) ;
330
334
for auth_url in order. authorizations {
331
335
let res = self . post ( & auth_url, b"" ) . await ?;
332
- let mut authorization: types:: Authorization = serde_json :: from_slice ( res. body ( ) ) ?;
336
+ let mut authorization: types:: Authorization = deserialize_body ( res. body ( ) ) ?;
333
337
334
338
authorization
335
339
. challenges
336
340
. retain ( |x| self . is_supported_challenge ( & x. kind ) ) ;
337
341
338
342
if authorization. challenges . is_empty ( ) {
339
- anyhow :: bail! ( "no supported challenge for {:?}" , authorization . identifier )
343
+ return Err ( NewCertificateError :: NoSupportedChallenges ) ;
340
344
}
341
345
342
346
match authorization. status {
@@ -351,11 +355,7 @@ where
351
355
authorization. identifier
352
356
) ;
353
357
}
354
- status => anyhow:: bail!(
355
- "unexpected authorization status for {:?}: {:?}" ,
356
- authorization. identifier,
357
- status
358
- ) ,
358
+ status => return Err ( NewCertificateError :: AuthorizationStatus ( status) ) ,
359
359
}
360
360
}
361
361
@@ -371,38 +371,48 @@ where
371
371
}
372
372
373
373
let mut res = self . post ( & order_url, b"" ) . await ?;
374
- let mut order: types:: Order = serde_json :: from_slice ( res. body ( ) ) ?;
374
+ let mut order: types:: Order = deserialize_body ( res. body ( ) ) ?;
375
375
376
376
if order. status != OrderStatus :: Ready {
377
- anyhow:: bail!( "not ready" ) ;
377
+ if let Some ( err) = order. error {
378
+ return Err ( err. into ( ) ) ;
379
+ }
380
+ return Err ( NewCertificateError :: OrderStatus ( order. status ) ) ;
378
381
}
379
382
380
- let csr = make_certificate_request ( & order. identifiers , & pkey) . and_then ( |x| x. to_der ( ) ) ?;
383
+ let csr = make_certificate_request ( & order. identifiers , & pkey)
384
+ . and_then ( |x| x. to_der ( ) )
385
+ . map_err ( NewCertificateError :: Csr ) ?;
381
386
let payload = std:: format!( r#"{{"csr":"{}"}}"# , crate :: jws:: base64url( csr) ) ;
382
387
383
388
match self . post ( & order. finalize , payload) . await {
384
389
Ok ( x) => {
385
390
drop ( order) ;
386
391
res = x;
387
- order = serde_json :: from_slice ( res. body ( ) ) ?;
392
+ order = deserialize_body ( res. body ( ) ) ?;
388
393
}
389
- Err ( err) => {
390
- if !err. to_string ( ) . contains ( "orderNotReady" ) {
391
- return Err ( err) ;
392
- }
393
- order. status = OrderStatus :: Processing
394
+ Err ( RequestError :: Protocol ( problem) )
395
+ if matches ! (
396
+ problem. category( ) ,
397
+ ProblemCategory :: Order | ProblemCategory :: Malformed
398
+ ) =>
399
+ {
400
+ return Err ( problem. into ( ) )
394
401
}
402
+ _ => order. status = OrderStatus :: Processing ,
395
403
} ;
396
404
397
405
let mut tries = backoff ( MAX_RETRY_INTERVAL , self . finalize_timeout ) ;
398
406
399
407
while order. status == OrderStatus :: Processing && wait_for_retry ( & res, & mut tries) . await {
400
408
drop ( order) ;
401
409
res = self . post ( & order_url, b"" ) . await ?;
402
- order = serde_json :: from_slice ( res. body ( ) ) ?;
410
+ order = deserialize_body ( res. body ( ) ) ?;
403
411
}
404
412
405
- let certificate = order. certificate . ok_or ( anyhow ! ( "certificate not ready" ) ) ?;
413
+ let certificate = order
414
+ . certificate
415
+ . ok_or ( NewCertificateError :: CertificateUrl ) ?;
406
416
407
417
let chain = self . post ( & certificate, b"" ) . await ?. into_body ( ) ;
408
418
@@ -414,7 +424,7 @@ where
414
424
order : & AuthorizationContext < ' _ > ,
415
425
url : http:: Uri ,
416
426
authorization : types:: Authorization ,
417
- ) -> Result < ( ) > {
427
+ ) -> Result < ( ) , NewCertificateError > {
418
428
let identifier = authorization. identifier . as_ref ( ) ;
419
429
420
430
// Find and set up first supported challenge.
@@ -425,7 +435,7 @@ where
425
435
let solver = self . find_solver_for ( & x. kind ) ?;
426
436
Some ( ( x, solver) )
427
437
} )
428
- . ok_or ( anyhow ! ( "no supported challenge for {identifier:?}" ) ) ?;
438
+ . ok_or ( NewCertificateError :: NoSupportedChallenges ) ?;
429
439
430
440
solver. register ( order, & identifier, challenge) ?;
431
441
@@ -434,20 +444,20 @@ where
434
444
} ;
435
445
436
446
let res = self . post ( & challenge. url , b"{}" ) . await ?;
437
- let result: types:: Challenge = serde_json :: from_slice ( res. body ( ) ) ?;
447
+ let result: types:: Challenge = deserialize_body ( res. body ( ) ) ?;
438
448
if !matches ! (
439
449
result. status,
440
450
ChallengeStatus :: Pending | ChallengeStatus :: Processing | ChallengeStatus :: Valid
441
451
) {
442
- return Err ( anyhow ! ( "unexpected challenge status {:?}" , result. status) ) ;
452
+ return Err ( NewCertificateError :: ChallengeStatus ( result. status ) ) ;
443
453
}
444
454
445
455
let mut tries = backoff ( MAX_RETRY_INTERVAL , self . authorization_timeout ) ;
446
456
wait_for_retry ( & res, & mut tries) . await ;
447
457
448
458
let result = loop {
449
459
let res = self . post ( & url, b"" ) . await ?;
450
- let result: types:: Authorization = serde_json :: from_slice ( res. body ( ) ) ?;
460
+ let result: types:: Authorization = deserialize_body ( res. body ( ) ) ?;
451
461
452
462
if result. status != AuthorizationStatus :: Pending
453
463
|| !wait_for_retry ( & res, & mut tries) . await
@@ -464,7 +474,7 @@ where
464
474
) ;
465
475
466
476
if result. status != AuthorizationStatus :: Valid {
467
- return Err ( anyhow ! ( "authorization failed ({:?})" , result. status) ) ;
477
+ return Err ( NewCertificateError :: AuthorizationStatus ( result. status ) ) ;
468
478
}
469
479
470
480
Ok ( ( ) )
@@ -539,6 +549,15 @@ fn backoff(max: Duration, timeout: Duration) -> impl Iterator<Item = Duration> {
539
549
. map ( move |( _, x) | x. min ( max) )
540
550
}
541
551
552
+ /// Deserializes JSON response body as T and converts error type.
553
+ #[ inline( always) ]
554
+ fn deserialize_body < ' a , T > ( bytes : & ' a Bytes ) -> Result < T , RequestError >
555
+ where
556
+ T : serde:: Deserialize < ' a > ,
557
+ {
558
+ serde_json:: from_slice ( bytes) . map_err ( RequestError :: ResponseFormat )
559
+ }
560
+
542
561
fn parse_retry_after ( val : & http:: HeaderValue ) -> Option < Duration > {
543
562
let val = val. to_str ( ) . ok ( ) ?;
544
563
0 commit comments