our
$_NONCE_HEADER
=
'replay-nonce'
;
our
$verify_SSL
= 1;
sub
new {
my
(
$class
,
%opts
) =
@_
;
my
$ua
= Net::ACME::HTTP_Tiny->new(
verify_SSL
=>
$verify_SSL
);
my
$self
=
bless
{
_ua
=>
$ua
,
_acme_key
=>
$opts
{
'key'
} && Net::ACME::Crypt::parse_key(
$opts
{
'key'
}),
},
$class
;
return
bless
$self
,
$class
;
}
sub
get {
my
(
$self
,
$url
) =
@_
;
return
$self
->_request_and_set_last_nonce(
'GET'
,
$url
);
}
sub
post {
my
(
$self
,
$url
,
$data
,
$opts_hr
) =
@_
;
die
"Constructor needed “key” to do POST! ($url)"
if
!
$self
->{
'_acme_key'
};
my
$jws
=
$self
->_create_jws( JSON->new()->allow_nonref()->encode(
$data
) );
return
$self
->_request_and_set_last_nonce(
'POST'
,
$url
,
{
content
=>
$jws
},
$opts_hr
|| (),
);
}
sub
_ua_request {
my
(
$self
,
$type
,
@args
) =
@_
;
return
$self
->{
'_ua'
}->request(
$type
,
@args
);
}
sub
_request {
my
(
$self
,
$type
,
@args
) =
@_
;
my
$resp
;
my
$eval_err
= $@;
eval
{
$resp
=
$self
->_ua_request(
$type
,
@args
); };
if
($@) {
my
$exc
= $@;
if
(
eval
{
$exc
->isa(
'Net::ACME::X::HTTP::Protocol'
) } ) {
my
$_nonce_header_lc
=
$_NONCE_HEADER
;
$_nonce_header_lc
=~
tr
<A-Z><a-z>;
my
$nonce
=
$exc
->get(
'headers'
)->{
$_nonce_header_lc
};
$self
->{
'_last_nonce'
} =
$nonce
if
$nonce
;
my
$acme_error
=
eval
{
Net::ACME::Error->new(
%{ JSON::decode_json(
$exc
->get(
'content'
) ) },
);
};
my
$detail
;
if
(
$acme_error
) {
$detail
=
$acme_error
->detail();
my
$desc
=
$acme_error
->description();
if
(
$desc
) {
$detail
=
sprintf
"%s (%s)"
,
$detail
,
$desc
;
}
}
else
{
$detail
=
$exc
->get(
'content'
);
}
die
Net::ACME::X::create(
'Protocol'
,
{
(
map
{
$_
=>
$exc
->get(
$_
) }
qw( url status reason headers )
),
type
=>
$acme_error
?
$acme_error
->type() :
'(unknown type)'
,
detail
=>
$detail
,
}
);
}
$@ =
$exc
;
die
;
}
$@ =
$eval_err
;
return
Net::ACME::HTTP::Response->new(
$resp
);
}
sub
_request_and_set_last_nonce {
my
(
$self
,
$type
,
@args
) =
@_
;
my
$resp
=
$self
->_request(
$type
,
@args
);
if
(
my
$nonce
=
$resp
->header(
$_NONCE_HEADER
) ) {
$self
->{
'_last_nonce'
} =
$nonce
;
}
return
$resp
;
}
sub
_create_jws {
my
(
$self
,
$msg
) =
@_
;
die
"Need a nonce before JWS can be created!"
if
!
$self
->{
'_last_nonce'
};
return
Net::ACME::Crypt::create_jwt(
key
=>
$self
->{
'_acme_key'
},
extra_headers
=> {
nonce
=>
$self
->{
'_last_nonce'
},
jwk
=>
$self
->{
'_acme_key'
}->get_struct_for_public_jwk(),
},
payload
=>
$msg
,
);
}
1;