Apache2-API
view release on metacpan or search on metacpan
lib/Apache2/API.pm view on Meta::CPAN
}
elsif( !$msg->{lang} && $self->_can( $msg->{message} => 'locale' ) )
{
$msg->{lang} = $msg->{message}->locale;
}
my $ctype = $self->response->content_type;
if( $ctype eq 'application/json' )
{
return( $self->reply( $msg->{code}, { error => $msg->{message} } ) );
}
else
{
# try-catch
local $@;
my $rv = eval
{
my $r = $self->apache_request;
$r->status( $msg->{code} );
$r->rflush;
$r->print( $msg->{message} );
return( $msg->{code} );
};
if( $@ )
{
return( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
}
return( $rv );
}
}
sub compression_threshold { return( shift->_set_get_number( 'compression_threshold', @_ ) ); }
# <https://perl.apache.org/docs/2.0/api/APR/Base64.html#toc_C_decode_>
sub decode_base64
{
my $self = shift( @_ );
my $data = shift( @_ );
# try-catch
local $@;
my $rv = eval
{
return( APR::Base64::decode( $data ) );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to base64 decode data: $@" ) );
}
return( $rv );
}
sub decode_json
{
my $self = shift( @_ );
my $raw = shift( @_ ) || return( $self->error( "No json data was provided to decode." ) );
my $json = $self->json;
my $hash;
# try-catch
local $@;
eval
{
$hash = $json->utf8->decode( $raw );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to decode json payload: $@" ) );
}
return( $hash );
}
sub decode_url
{
my $self = shift( @_ );
return( APR::Request::decode( shift( @_ ) ) );
}
sub decode_utf8
{
my $self = shift( @_ );
my $v = shift( @_ );
my $rv = eval
{
## utf8 is more lax than the strict standard of utf-8; see Encode man page
Encode::decode( 'utf8', $v, Encode::FB_CROAK );
};
if( $@ )
{
$self->error( "Error while decoding text: $@" );
return( $v );
}
return( $rv );
}
# https://perl.apache.org/docs/2.0/api/APR/Base64.html#toc_C_encode_
# sub encode_base64 { return( APR::Base64::encode( @_ ) ); }
sub encode_base64
{
my $self = shift( @_ );
my $data = shift( @_ );
return( $self->error( "No valid to base64 encode was provided." ) ) if( !length( $data ) );
# try-catch
local $@;
my $rv = eval
{
return( APR::Base64::encode( $data ) );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to base64 encode data: $@" ) );
}
return( $rv );
}
sub encode_json
{
my $self = shift( @_ );
my $hash = shift( @_ ) || return( $self->error( "No perl hash reference was provided to encode." ) );
return( $self->error( "Hash provided ($hash) is not a hash reference." ) ) if( !$self->_is_hash( $hash ) );
my $json = $self->json->allow_nonref->allow_blessed->convert_blessed->relaxed;
my $data;
# try-catch
local $@;
eval
{
$data = $json->encode( $hash );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to encode perl data: $@\nPerl data are: ", sub{ $self->SUPER::dump( $hash ) } ) );
}
return( $data );
}
sub encode_url
{
my $self = shift( @_ );
return( APR::Request::encode( shift( @_ ) ) );
}
sub encode_utf8
{
my $self = shift( @_ );
my $v = shift( @_ );
my $rv = eval
{
## utf8 is more lax than the strict standard of utf-8; see Encode man page
Encode::encode( 'utf8', $v, Encode::FB_CROAK );
};
if( $@ )
{
$self->error( "Error while encoding text: $@" );
return( $v );
}
return( $rv );
}
# <https://perl.apache.org/docs/2.0/api/APR/UUID.html>
sub generate_uuid
{
my $self = shift( @_ );
# try-catch
local $@;
my $rv = eval
{
return( APR::UUID->new->format );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to generate an uuid using APR::UUID package: $@" ) );
}
return( $rv );
}
# rfc 6750 <https://tools.ietf.org/html/rfc6750>
sub get_auth_bearer
{
my $self = shift( @_ );
my $bearer = $self->request->authorization;
# Found a bearer
if( $bearer )
{
# https://jwt.io/introduction/
# https://tools.ietf.org/html/rfc7519
# if( $bearer =~ /^Bearer[[:blank:]]+([a-zA-Z0-9][a-zA-Z0-9\-\_\~\+\/\=]+(?:\.[a-zA-Z0-9\_][a-zA-Z0-9\-\_\~\+\/\=]+){2,4})$/i )
if( $bearer =~ /^Bearer[[:blank:]]+([a-zA-Z0-9][a-zA-Z0-9\-\_\~\+\/\=]+(?:\.[a-zA-Z0-9\_][a-zA-Z0-9\-\_\~\+\/\=]+)*)$/i )
{
my $token = $1;
return( $token );
}
else
{
return( $self->error({ code => Apache2::Const::HTTP_BAD_REQUEST, message => "Bad bearer authorization format" }) );
}
}
else
{
# Return empty, not undef, because undef is for errors
return( '' );
}
}
# <https://perl.apache.org/docs/2.0/api/Apache2/ServerUtil.html>
sub get_handlers { return( shift->_try( 'server', 'get_handlers', @_ ) ); }
# Does nothing and it should be superseded by a class inheriting our module
# This gives a chance to return a localised version of our string to the user
sub gettext { return( $_[1] ); }
sub header_datetime
{
my $self = shift( @_ );
my $dt;
if( @_ )
{
return( $self->error( "Date time provided (", ( $_[0] // 'undef' ), ") is not an object." ) ) if( !Scalar::Util::blessed( $_[0] ) );
return( $self->error( "Object provided (", ref( $_[0] ), ") is not a DateTime or DateTime::Lite object." ) ) if( !$_[0]->isa( 'DateTime' ) && !$_[0]->isa( 'DateTime::Lite' ) );
$dt = shift( @_ );
}
$dt = DateTime::Lite->now if( !defined( $dt ) );
my $fmt = Apache2::API::DateTime->new;
$dt->set_formatter( $fmt );
return( $dt );
}
sub htpasswd
{
my $self = shift( @_ );
my $rv = Apache2::API::Password->new( @_ );
if( !defined( $rv ) && Apache2::API::Password->error )
{
return( $self->pass_error( Apache2::API::Password->error ) );
}
return( $rv );
}
sub is_perl_option_enabled { return( shift->_try( 'request', 'is_perl_option_enabled', @_ ) ); }
# We return a new object each time, because if we cached it, some routine might set the utf8 bit flagged on while some other would not want it
sub json
{
my $self = shift( @_ );
my $opts = $self->_get_args_as_hash( @_ );
my $j = JSON->new;
my $equi =
{
ordered => 'canonical',
sorted => 'canonical',
sort => 'canonical',
};
foreach my $opt ( keys( %$opts ) )
{
my $ref;
$ref = $j->can( exists( $equi->{ $opt } ) ? $equi->{ $opt } : $opt ) || do
{
warn( "Unknown JSON option '${opt}'\n" ) if( $self->_warnings_is_enabled );
next;
};
$ref->( $j, $opts->{ $opt } );
}
return( $j );
}
sub lang { return( shift->_set_get_scalar( 'lang', @_ ) ); }
sub lang_unix
{
my $self = shift( @_ );
my $lang = $self->{lang};
$lang =~ tr/-/_/;
return( $lang );
}
sub lang_web
{
my $self = shift( @_ );
my $lang = $self->{lang};
$lang =~ tr/_/-/;
return( $lang );
}
# Would return a Apache2::Log::Request
sub log { return( shift->_try( 'apache_request', 'log', @_ ) ); }
sub log_error { return( shift->_try( 'apache_request', 'log_error', @_ ) ); }
sub print
{
my $self = shift( @_ );
my $opts = {};
if( scalar( @_ ) == 1 && ref( $_[0] ) )
{
$opts = shift( @_ );
}
else
{
$opts->{data} = join( '', @_ );
}
return( $self->error( "No data was provided to print out." ) ) if( !CORE::length( $opts->{data} ) );
my $r = $self->apache_request;
my $json = $opts->{data};
my $bytes = 0;
# Before we use this, we have to make sure all Apache module that deal with content encoding are de-activated because they would interfere
my $threshold = $self->compression_threshold || 0;
# rfc1952
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
my $z;
if( CORE::length( $json ) > $threshold &&
$self->request->accept_encoding =~ /\bgzip\b/i &&
$self->_load_class( 'IO::Compress::Gzip' ) &&
( $z = IO::Compress::Gzip->new( '-', Minimal => 1 ) ) )
{
#require Compress::Zlib;
#$r->print( Compress::Zlib::memGzip( $json ) );
# $r->content_encoding( 'gzip' );
$self->response->content_encoding( 'gzip' );
$self->response->headers->set( 'Content-Encoding' => 'gzip' );
# Why Vary? https://blog.stackpath.com/accept-encoding-vary-important/
# We use merge, because another value may already be set
$self->response->headers->merge( 'Vary' => 'Accept-Encoding' );
# $r->send_http_header;
$z->print( $json );
$z->close;
}
elsif( CORE::length( $json ) > $threshold &&
$self->request->accept_encoding =~ /\bbzip2\b/i &&
$self->_load_class( 'IO::Compress::Bzip2' ) &&
( $z = IO::Compress::Bzip2->new( '-' ) ) )
{
# $r->content_encoding( 'bzip2' );
$self->response->content_encoding( 'bzip2' );
$self->response->headers->set( 'Content-Encoding' => 'bzip2' );
$self->response->headers->merge( 'Vary' => 'Accept-Encoding' );
# $r->send_http_header;
$z->print( $json );
$z->close;
}
elsif( CORE::length( $json ) > $threshold &&
$self->request->accept_encoding =~ /\bdeflate\b/i &&
$self->_load_class( 'IO::Compress::Deflate' ) &&
( $z = IO::Compress::Deflate->new( '-' ) ) )
{
## $r->content_encoding( 'deflate' );
$self->response->content_encoding( 'deflate' );
$self->response->headers->set( 'Content-Encoding' => 'deflate' );
$self->response->headers->merge( 'Vary' => 'Accept-Encoding' );
# $r->send_http_header;
$z->print( $json );
$z->close;
}
else
{
$self->response->headers->unset( 'Content-Encoding' );
# $self->response->content_encoding( undef() );
# $r->send_http_header;
# $r->print( $json );
# $json = Encode::encode_utf8( $json ) if( utf8::is_utf8( $json ) );
# try-catch
local $@;
eval
{
my $bytes = $r->print( $json );
};
if( $@ )
{
}
}
# $r->rflush;
# Flush any buffered data to the client using Apache2::RequestIO
$self->response->rflush;
return( $self );
}
# push_handlers($hook_name => \&handler);
# push_handlers($hook_name => [\&handler, \&handler2]);
sub push_handlers { return( shift->_try( 'server', 'push_handlers', @_ ) ); }
# See also <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/406>
sub reply
{
my $self = shift( @_ );
my( $code, $ref );
my $use_rfc_error = $self->{use_rfc_error} // $USE_RFC_ERROR;
# rfc9457 standard for REST API error response: <https://www.rfc-editor.org/rfc/rfc9457.html>
# Legacy JSON payload like Google, Twitter, Facebook
# Modern REST APIs now uses rfc9457 with a flattened payload.
# When the use_rfc_error object property is true, we use rfc9457 flattened error, this will produce something like:
# {
# error => 'not_found',
# status => 404,
# title => 'Not found!',
# detail => q{The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again.},
# locale => 'en-US',
# type => 'https://api.example.com/problems/not-found',
# }
# otherwise, the legacy approach would be:
# {
# error =>
# {
# code => 404,
# message => q{The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again.},
# },
# locale => 'en-US',
# }
# $self->reply( Apache2::Const::HTTP_OK, { message => "All is well" } );
if( scalar( @_ ) == 2 )
{
( $code, $ref ) = @_;
}
elsif( scalar( @_ ) == 1 &&
$self->_can( $_[0] => 'code' ) &&
$self->_can( $_[0] => 'message' ) )
{
my $ex = shift( @_ );
$code = $ex->code;
$ref =
{
message => $ex->message,
( $ex->can( 'public_message' ) ? ( public_message => $ex->public_message ) : () ),
( $ex->can( 'locale' ) ? ( locale => $ex->locale ) : () ),
};
}
# $self->reply({ code => Apache2::Const::HTTP_OK, message => "All is well" } );
elsif( ref( $_[0] ) eq 'HASH' )
{
$ref = shift( @_ );
$code = $ref->{code} if( length( $ref->{code} ) );
}
my $r = $self->apache_request;
my $req = $self->request;
my $resp = $self->response;
# Guardrails on inputs
if( !defined( $code ) || $code !~ /^[0-9]{3}$/ )
{
$resp->code( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
$resp->rflush;
$resp->print( $self->json->utf8->encode({ error => 'An unexpected server error occured', code => 500 }) );
$self->error( "http code to be used '", ( $code // 'undef' ), "' is invalid. It should be only integers." );
return( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
}
if( ref( $ref ) ne 'HASH' )
{
$resp->code( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
$resp->rflush;
# $r->send_http_header;
$resp->print( $self->json->utf8->encode({ error => 'An unexpected server error occured', code => 500 }) );
$self->error( "Data provided to send is not a hash ref." );
return( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
}
# Resolve whether this is an error
my $is_error = $resp->is_error( $code ) ? 1 : 0;
# NOTE: guess_preferred_locale() -> this is used to get he most appropriate locale if not defined already so we can, in turn, get the fallback description
my $guess_preferred_locale = sub
{
my $locale = shift( @_ );
if( !defined( $locale ) )
{
$locale = $req->preferred_language( Apache2::API::Status->supported_languages );
}
if( defined( $locale ) )
{
# Make sure we are dealing with unix style language code
$locale =~ tr/-/_/;
if( length( $locale ) == 2 )
{
$locale = Apache2::API::Status->convert_short_lang_to_long( $locale );
}
# We have something weird, like maybe eng?
elsif( $locale !~ /^[a-z]{2}_[A-Z]{2}$/ )
{
$locale = Apache2::API::Status->convert_short_lang_to_long( substr( $locale, 0, 2 ) );
}
}
return( $locale );
};
# NOTE: build_rfc_error() -> private subroutine to build the modern rfc9457 error payload
my $build_rfc_error = sub
{
my( $ref, $code, $msg ) = @_;
# By now, our property 'locale' has been dealt with, so we do not have to worry about it.
# It either exists or not
my $locale = exists( $ref->{locale} ) ? $ref->{locale} : undef;
# The property 'status' could exist, but be undefined, or even empty, so we check for that.
unless( exists( $ref->{status} ) &&
defined( $ref->{status} ) &&
length( $ref->{status} ) )
{
$ref->{status} = $code;
}
$ref->{status} = int( $ref->{status} ) if( $ref->{status} =~ /^\d+$/ );
# Title from caller or from HTTP status table (localized)
unless( exists( $ref->{title} ) &&
defined( $ref->{title} ) &&
length( $ref->{title} // '' ) )
{
if( exists( $ref->{error} ) &&
defined( $ref->{error} ) &&
ref( $ref->{error} ) eq 'HASH' &&
exists( $ref->{error}->{title} ) )
{
lib/Apache2/API.pm view on Meta::CPAN
# If we have a locale set, we use it
my $locale;
if( $is_error )
{
if( $use_rfc_error )
{
$locale = $ref->{locale} if( exists( $ref->{locale} ) );
}
else
{
$locale = $ref->{error}->{locale} if( exists( $ref->{error} ) && ref( $ref->{error} ) eq 'HASH' && exists( $ref->{error}->{locale} ) );
}
}
# Success response
else
{
$locale = $ref->{locale} if( exists( $ref->{locale} ) );
}
if( $locale )
{
# Set the content language for this payload unless the user has already set it.
unless( $resp->headers->get( 'Content-Language' ) )
{
# en_GB -> en-GB
( my $hdr_locale = $locale ) =~ tr/_/-/;
$resp->headers->set( 'Content-Language' => $hdr_locale );
}
$resp->headers->merge( 'Vary' => 'Accept-Language' );
}
# Choose Content-Type
# If we use new modern error, then we set application/problem+json in line with rfc7807
my $ctype = ( $is_error && $use_rfc_error )
? 'application/problem+json; charset=utf-8'
: 'application/json; charset=utf-8';
$resp->content_type( $ctype );
# $r->status( $code );
$resp->code( $code );
if( defined( $msg ) && $ctype !~ m{^application/(?:json|problem\+json)}i )
{
$resp->custom_response( $code, $msg );
}
else
{
$resp->custom_response( $code, '' );
#$r->status( $code );
}
if( exists( $ref->{cleanup} ) &&
defined( $ref->{cleanup} ) &&
ref( $ref->{cleanup} ) eq 'CODE' )
{
my $cleanup = delete( $ref->{cleanup} );
# See <https://perl.apache.org/docs/2.0/user/handlers/http.html#PerlCleanupHandler>
$r->pool->cleanup_register( $cleanup, $self );
}
# Our print() will possibly change the HTTP headers, so we do not flush now just yet.
my $json = $self->json->utf8->relaxed(0)->allow_blessed->convert_blessed->encode( $ref );
# Before we use this, we have to make sure all Apache module that deal with content encoding are de-activated because they would interfere
if( !$self->print( $json ) )
{
return( Apache2::Const::HTTP_INTERNAL_SERVER_ERROR );
}
return( $code );
}
# Special reply for Server-Sent Event that need to close the connection if there was an error
sub reply_sse
{
my $self = shift( @_ );
my $code = $self->reply( @_ );
$code //= 500;
if( Apache2::API::Status->is_error( $code ) )
{
my $req = $self->request;
$req->request->pool->cleanup_register(sub
{
$req->close;
});
}
return( $code );
}
sub request { return( shift->_set_get_object( 'request', 'Apache2::API::Request', @_ ) ); }
sub response { return( shift->_set_get_object( 'response', 'Apache2::API::Response', @_ ) ); }
sub server
{
my $self = shift( @_ );
# try-catch
local $@;
my $rv = eval
{
my $r = $self->apache_request;
return( $r->server ) if( $r );
return( Apache2::ServerUtil->server );
};
if( $@ )
{
return( $self->error( "An error occurred while trying to get the Apache server object: $@" ) );
}
return( $rv );
}
# sub server_version { return( version->parse( Apache2::ServerUtil::get_server_version ) ); }
# Or maybe the environment variable SERVER_SOFTWARE, e.g. Apache/2.4.18
# sub server_version { return( version->parse( Apache2::ServerUtil::get_server_version ) ); }
sub server_version
{
my $self = shift( @_ );
my $v = $self->request->server_version || return( $self->pass_error( $self->request->error ) );
return( version->parse( $v ) );
}
# $ok = $s->set_handlers($hook_name => \&handler);
# $ok = $s->set_handlers($hook_name => [\&handler, \&handler2]);
# $ok = $s->set_handlers($hook_name => []);
lib/Apache2/API.pm view on Meta::CPAN
my $self = shift( @_ );
my( $v, $n, $itoa64 ) = @_;
my $s = '';
while( $n-- > 0 )
{
$s .= substr( $itoa64, $v & 0x3f, 1 );
$v >>= 6;
}
return( $s );
}
1;
# NOTE: POD
__END__
=encoding utf-8
=head1 NAME
Apache2::API - Apache2 API Framework
=head1 SYNOPSIS
use Apache2::API
# To import in your namespace
# use Apache2::API qw( :common :http );
# $r is an Apache2::RequestRec object that you can get from within an handler or
# with Apache2::RequestUtil->request
my $api = Apache2::API->new( $r, compression_threshold => 204800 ) ||
die( Apache2::API->error );
# or:
my $api = Apache2::API->new( apache_request => $r, compression_threshold => 204800 ) ||
die( Apache2::API->error );
# or even inside your mod_perl script/cgi:
#!/usr/bin/perl
use strict;
use warnings;
use Apache2::API;
my $r = shift( @_ );
my $api = Apache2::API->new( $r );
# for example:
return( $api->reply( Apache2::Const::HTTP_OK => { message => "Hello world" } ) );
my $r = $api->apache_request;
return( $api->bailout({
message => "Oops",
code => Apache2::Const::BAD_REQUEST,
public_message => "An unexpected error occurred.",
}) );
# or
return( $api->bailout( @some_reasons ) );
# 100kb
$api->compression_threshold(102400);
my $decoded = $api->decode_base64( $b64_string );
my $ref = $api->decode_json( $json_data );
my $decoded = $api->decode_url;
my $perl_utf8 = $api->decode_utf8( $data );
my $b64_string = $api->encode_base64( $data );
my $json_data = $api->encode_json( $ref );
my $encoded = $api->encode_url( $uri );
my $utf8 = $api->encode_utf8( $data );
my $uuid = $api->generate_uuid;
my $auth = $api->get_auth_bearer;
my $handlers = $api->get_handlers;
my $dt = $api->header_datetime( $http_datetime );
my $bool = $api->is_perl_option_enabled;
# JSON object
my $json = $api->json( pretty => 1, sorted => 1, relaxed => 1 );
my $lang = $api->lang( 'en_GB' );
# en_GB
my $lang = $api->lang_unix;
# en-GB
my $lang = $api->lang_web;
$api->log_error( "Oops" );
$api->print( @some_data );
$api->push_handlers( $name => $code_reference );
return( $api->reply( Apache2::Const::HTTP_OK => {
message => "All good!",
# arbitrary property
client_id => "efe4bcf3-730c-4cb2-99df-25d4027ec404",
# special property
cleanup => sub
{
# Some code here to be executed after the reply is sent out to the client.
}
}) );
# Apache2::API::Request
my $req = $api->request;
# Apache2::API::Response
my $req = $api->response;
my $server = $api->server;
my $version = $api->server_version;
$api->set_handlers( $name => $code_reference );
$api->warn( @some_warnings );
my $hash = apr1_md5( $clear_password );
my $hash = apr1_md5( $clear_password, $salt );
my $ht = $api->htpasswd( $clear_password );
my $ht = $api->htpasswd( $clear_password, salt => $salt );
my $hash = $ht->hash;
say "Does our password match ? ", $ht->matches( $user_clear_password ) ? "yes" : "not";
=head1 VERSION
v0.5.4
=head1 DESCRIPTION
This module provides a comprehensive, powerful, yet simple framework to access L<Apache mod_perl's API|https://perl.apache.org/docs/2.0/api/> and documented appropriately.
Apache mod_perl is an awesome framework, but quite complexe with a steep learning curve and methods all over the place. So much so that L<they have developed a module dedicated to find appropriate methods|https://perl.apache.org/docs/2.0/user/coding/...
=head1 METHODS
=head2 new
my $api = Apache2::API->new( $r, $hash_ref_of_options );
# or
my $api = Apache2::API->new( apache_request => $r, compression_threshold => 102400 );
This initiates the package and takes an L<Apache2::RequestRec> object and an hash or hash reference of parameters, or only an hash or hash reference of parameters:
=over 4
=item * C<apache_request>
See L</apache_request>
=item * C<compression_threshold>
See L</compression_threshold>
=item * C<debug>
Optional. If set with a positive integer, this will activate debugging message
=back
=head2 apache_request
Returns the L<Apache2::RequestRec> object that was provided upon object instantiation.
=head2 bailout
$api->bailout( $error_string );
$api->bailout( { code => 400, message => $internal_message } );
$api->bailout( { code => 400, message => $internal_message, public_message => "Sorry!" } );
Given an error message, this will prepare the HTTP header and response accordingly.
It will call L</gettext> to get the localised version of the error message, so this method is expected to be overriden by inheriting package.
If the outgoing content type set is C<application/json> then this will return a properly formatted standard json error, such as:
{ "error": { "code": 401, "message": "Something went wrong" } }
Otherwise, it will send to the client the message as is.
=head2 compression_threshold( $integer )
The number of bytes threshold beyond which, the L</reply> method will gzip compress the data returned to the client.
=head2 decode_base64( $data )
Given some data, this will decode it using base64 algorithm. It uses L<APR::Base64/decode> in the background.
=head2 decode_json( $data )
This decode from utf8 some data into a perl structure using L<JSON>
If an error occurs, it will return undef and set an exception that can be accessed with the L<error|Module::Generic/error> method.
=head2 decode_url( $string )
Given a url-encoded string, this returns the decoded string using L<APR::Request/decode>
=head2 decode_utf8( $data )
Decode some data from ut8 into perl internal utf8 representation using L<Encode>
If an error occurs, it will return undef and set an exception that can be accessed with the L<error|Module::Generic/errir> method.
=head2 encode_base64( $data )
Given some data, this will encode it using base64 algorithm. It uses L<APR::Base64/encode>.
=head2 encode_json( $hash_reference )
Given a hash reference, this will encode it into a json data representation.
However, this will not utf8 encode it, because this is done upon printing the data and returning it to the client.
The JSON object has the following properties enabled: C<allow_nonref>, C<allow_blessed>, C<convert_blessed> and C<relaxed>
=head2 encode_url( $string )
Given a string, this returns its url-encoded version using L<APR::Request/encode>
=head2 encode_utf8( $data )
This encode in ut8 the data provided and return it.
If an error occurs, it will return undef and set an exception that can be accessed with the B<error> method.
=head2 generate_uuid
Generates an uuid string and return it. This uses L<APR::UUID>
=head2 get_auth_bearer
Checks whether an C<Authorization> HTTP header was provided, and get the Bearer value.
If no header was found, it returns an empty string.
If an error occurs, it will return undef and set an exception that can be accessed with the B<error> method.
=head2 get_handlers
Returns a reference to a list of handlers enabled for a given phase.
$handlers_list = $res->get_handlers( $hook_name );
A list of handlers configured to run at the child_exit phase:
@handlers = @{ $res->get_handlers( 'PerlChildExitHandler' ) || []};
=head2 gettext( 'string id' )
Get the localised version of the string passed as an argument.
This is supposed to be superseded by the package inheriting from L<Apache2::API>, if any.
=head2 header_datetime( DateTime or DateTime::Lite object )
Given a L<DateTime> or L<DateTime::Lite> object, this sets it to GMT time zone and set the proper formatter (L<Apache2::API::DateTime>) so that the stringification is compliant with HTTP headers standard.
=head2 htpasswd
my $ht = $api->htpasswd( $clear_password, create => 1 );
my $ht = $api->htpasswd( $clear_password, create => 1, salt => $salt );
my $ht = $api->htpasswd( $md5_password );
my $bool = $ht->matches( $user_input_password );
This instantiates a new L<Apache2::API::Password> object by providing its constructor whatever arguments was received.
It returns a new L<Apache2::API::Password> object, or, upon error, C<undef> in scalar context, or an empty list in list context.
=head2 is_perl_option_enabled
Checks if perl option is enabled in the Virtual Host and returns a boolean value
=head2 json
Returns a JSON object.
You can provide an optional hash or hash reference of properties to enable or disable:
my $J = $api->json( pretty => 1, relaxed => 1 );
( run in 1.390 second using v1.01-cache-2.11-cpan-13bb782fe5a )