@@ -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:: { AccountStatus , 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,21 @@ 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 account : types :: Account = deserialize_body ( res. body ( ) ) ? ;
291
+ if ! matches ! ( account . status , AccountStatus :: Valid ) {
292
+ return Err ( NewAccountError :: Status ( account . status ) ) ;
293
+ }
294
+
295
+ let key_id : & str =
296
+ try_get_header ( res . headers ( ) , http :: header :: LOCATION ) . ok_or ( NewAccountError :: Url ) ? ;
287
297
288
- Ok ( serde_json:: from_slice ( res. body ( ) ) ?)
298
+ self . account = Some ( key_id. to_string ( ) ) ;
299
+
300
+ Ok ( ( ) )
289
301
}
290
302
291
303
pub fn is_ready ( & self ) -> bool {
@@ -295,7 +307,7 @@ where
295
307
pub async fn new_certificate < A > (
296
308
& self ,
297
309
req : & CertificateOrder < & str , A > ,
298
- ) -> Result < NewCertificateOutput >
310
+ ) -> Result < NewCertificateOutput , NewCertificateError >
299
311
where
300
312
A : Allocator ,
301
313
{
@@ -313,30 +325,27 @@ where
313
325
not_after : None ,
314
326
} ;
315
327
316
- let payload = serde_json:: to_string ( & payload) ?;
328
+ let payload = serde_json:: to_string ( & payload) . map_err ( RequestError :: RequestFormat ) ?;
317
329
318
330
let res = self . post ( & self . directory . new_order , payload) . await ?;
319
331
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" ) ) ?;
332
+ let order_url = try_get_header ( res. headers ( ) , http:: header:: LOCATION )
333
+ . and_then ( |x| Uri :: try_from ( x) . ok ( ) )
334
+ . ok_or ( NewCertificateError :: OrderUrl ) ?;
325
335
326
- let order_url = Uri :: try_from ( order_url) ?;
327
- let order: types:: Order = serde_json:: from_slice ( res. body ( ) ) ?;
336
+ let order: types:: Order = deserialize_body ( res. body ( ) ) ?;
328
337
329
338
let mut authorizations: Vec < ( http:: Uri , types:: Authorization ) > = Vec :: new ( ) ;
330
339
for auth_url in order. authorizations {
331
340
let res = self . post ( & auth_url, b"" ) . await ?;
332
- let mut authorization: types:: Authorization = serde_json :: from_slice ( res. body ( ) ) ?;
341
+ let mut authorization: types:: Authorization = deserialize_body ( res. body ( ) ) ?;
333
342
334
343
authorization
335
344
. challenges
336
345
. retain ( |x| self . is_supported_challenge ( & x. kind ) ) ;
337
346
338
347
if authorization. challenges . is_empty ( ) {
339
- anyhow :: bail! ( "no supported challenge for {:?}" , authorization . identifier )
348
+ return Err ( NewCertificateError :: NoSupportedChallenges ) ;
340
349
}
341
350
342
351
match authorization. status {
@@ -351,11 +360,7 @@ where
351
360
authorization. identifier
352
361
) ;
353
362
}
354
- status => anyhow:: bail!(
355
- "unexpected authorization status for {:?}: {:?}" ,
356
- authorization. identifier,
357
- status
358
- ) ,
363
+ status => return Err ( NewCertificateError :: AuthorizationStatus ( status) ) ,
359
364
}
360
365
}
361
366
@@ -371,38 +376,48 @@ where
371
376
}
372
377
373
378
let mut res = self . post ( & order_url, b"" ) . await ?;
374
- let mut order: types:: Order = serde_json :: from_slice ( res. body ( ) ) ?;
379
+ let mut order: types:: Order = deserialize_body ( res. body ( ) ) ?;
375
380
376
381
if order. status != OrderStatus :: Ready {
377
- anyhow:: bail!( "not ready" ) ;
382
+ if let Some ( err) = order. error {
383
+ return Err ( err. into ( ) ) ;
384
+ }
385
+ return Err ( NewCertificateError :: OrderStatus ( order. status ) ) ;
378
386
}
379
387
380
- let csr = make_certificate_request ( & order. identifiers , & pkey) . and_then ( |x| x. to_der ( ) ) ?;
388
+ let csr = make_certificate_request ( & order. identifiers , & pkey)
389
+ . and_then ( |x| x. to_der ( ) )
390
+ . map_err ( NewCertificateError :: Csr ) ?;
381
391
let payload = std:: format!( r#"{{"csr":"{}"}}"# , crate :: jws:: base64url( csr) ) ;
382
392
383
393
match self . post ( & order. finalize , payload) . await {
384
394
Ok ( x) => {
385
395
drop ( order) ;
386
396
res = x;
387
- order = serde_json :: from_slice ( res. body ( ) ) ?;
397
+ order = deserialize_body ( res. body ( ) ) ?;
388
398
}
389
- Err ( err) => {
390
- if !err. to_string ( ) . contains ( "orderNotReady" ) {
391
- return Err ( err) ;
392
- }
393
- order. status = OrderStatus :: Processing
399
+ Err ( RequestError :: Protocol ( problem) )
400
+ if matches ! (
401
+ problem. category( ) ,
402
+ ProblemCategory :: Order | ProblemCategory :: Malformed
403
+ ) =>
404
+ {
405
+ return Err ( problem. into ( ) )
394
406
}
407
+ _ => order. status = OrderStatus :: Processing ,
395
408
} ;
396
409
397
410
let mut tries = backoff ( MAX_RETRY_INTERVAL , self . finalize_timeout ) ;
398
411
399
412
while order. status == OrderStatus :: Processing && wait_for_retry ( & res, & mut tries) . await {
400
413
drop ( order) ;
401
414
res = self . post ( & order_url, b"" ) . await ?;
402
- order = serde_json :: from_slice ( res. body ( ) ) ?;
415
+ order = deserialize_body ( res. body ( ) ) ?;
403
416
}
404
417
405
- let certificate = order. certificate . ok_or ( anyhow ! ( "certificate not ready" ) ) ?;
418
+ let certificate = order
419
+ . certificate
420
+ . ok_or ( NewCertificateError :: CertificateUrl ) ?;
406
421
407
422
let chain = self . post ( & certificate, b"" ) . await ?. into_body ( ) ;
408
423
@@ -414,7 +429,7 @@ where
414
429
order : & AuthorizationContext < ' _ > ,
415
430
url : http:: Uri ,
416
431
authorization : types:: Authorization ,
417
- ) -> Result < ( ) > {
432
+ ) -> Result < ( ) , NewCertificateError > {
418
433
let identifier = authorization. identifier . as_ref ( ) ;
419
434
420
435
// Find and set up first supported challenge.
@@ -425,7 +440,7 @@ where
425
440
let solver = self . find_solver_for ( & x. kind ) ?;
426
441
Some ( ( x, solver) )
427
442
} )
428
- . ok_or ( anyhow ! ( "no supported challenge for {identifier:?}" ) ) ?;
443
+ . ok_or ( NewCertificateError :: NoSupportedChallenges ) ?;
429
444
430
445
solver. register ( order, & identifier, challenge) ?;
431
446
@@ -434,20 +449,20 @@ where
434
449
} ;
435
450
436
451
let res = self . post ( & challenge. url , b"{}" ) . await ?;
437
- let result: types:: Challenge = serde_json :: from_slice ( res. body ( ) ) ?;
452
+ let result: types:: Challenge = deserialize_body ( res. body ( ) ) ?;
438
453
if !matches ! (
439
454
result. status,
440
455
ChallengeStatus :: Pending | ChallengeStatus :: Processing | ChallengeStatus :: Valid
441
456
) {
442
- return Err ( anyhow ! ( "unexpected challenge status {:?}" , result. status) ) ;
457
+ return Err ( NewCertificateError :: ChallengeStatus ( result. status ) ) ;
443
458
}
444
459
445
460
let mut tries = backoff ( MAX_RETRY_INTERVAL , self . authorization_timeout ) ;
446
461
wait_for_retry ( & res, & mut tries) . await ;
447
462
448
463
let result = loop {
449
464
let res = self . post ( & url, b"" ) . await ?;
450
- let result: types:: Authorization = serde_json :: from_slice ( res. body ( ) ) ?;
465
+ let result: types:: Authorization = deserialize_body ( res. body ( ) ) ?;
451
466
452
467
if result. status != AuthorizationStatus :: Pending
453
468
|| !wait_for_retry ( & res, & mut tries) . await
@@ -464,7 +479,7 @@ where
464
479
) ;
465
480
466
481
if result. status != AuthorizationStatus :: Valid {
467
- return Err ( anyhow ! ( "authorization failed ({:?})" , result. status) ) ;
482
+ return Err ( NewCertificateError :: AuthorizationStatus ( result. status ) ) ;
468
483
}
469
484
470
485
Ok ( ( ) )
@@ -539,6 +554,15 @@ fn backoff(max: Duration, timeout: Duration) -> impl Iterator<Item = Duration> {
539
554
. map ( move |( _, x) | x. min ( max) )
540
555
}
541
556
557
+ /// Deserializes JSON response body as T and converts error type.
558
+ #[ inline( always) ]
559
+ fn deserialize_body < ' a , T > ( bytes : & ' a Bytes ) -> Result < T , RequestError >
560
+ where
561
+ T : serde:: Deserialize < ' a > ,
562
+ {
563
+ serde_json:: from_slice ( bytes) . map_err ( RequestError :: ResponseFormat )
564
+ }
565
+
542
566
fn parse_retry_after ( val : & http:: HeaderValue ) -> Option < Duration > {
543
567
let val = val. to_str ( ) . ok ( ) ?;
544
568
0 commit comments