our
$VERSION
=
'1.09'
;
my
%dispatch_builder_map
= (
pod
=> \
&build_dispatcher_from_pod
,
config
=> \
&build_dispatcher_from_config
,
);
register PLUGIN_NAME ,=>
sub
{
my
(
$self
,
$base_url
,
$arguments
) = plugin_args(
@_
);
my
$publisher
;
GIVEN: {
local
$_
=
$arguments
->{publish} //
'config'
;
exists
(
$dispatch_builder_map
{
$_
}) &&
do
{
$publisher
=
$dispatch_builder_map
{
$_
};
$arguments
->{arguments} = plugin_setting()
if
$_
eq
'config'
;
last
GIVEN;
};
do
{
$publisher
=
$_
;
};
}
my
$dispatcher
=
$publisher
->(
$arguments
->{arguments},
$base_url
);
my
$lister
= Dancer::RPCPlugin::DispatchMethodList->new();
$lister
->set_partial(
protocol
=> PLUGIN_NAME,
endpoint
=>
$base_url
,
methods
=> [
sort
keys
%{
$dispatcher
} ],
);
my
$code_wrapper
=
$arguments
->{code_wrapper}
?
$arguments
->{code_wrapper}
:
sub
{
my
$code
=
shift
;
my
$pkg
=
shift
;
$code
->(
@_
);
};
my
$callback
=
$arguments
->{callback};
debug(
"Starting restrpc-handler build: "
,
$lister
);
my
$handle_call
=
sub
{
my
(
$ct
) = (
split
/;\s*/, request->content_type, 2);
if
(
$ct
ne
'application/json'
) {
pass();
}
debug(
"[handle_restrpc_request] Processing: "
, request->body);
my
(
$method_name
) = request->path =~ m{
$base_url
/(\w+)};
if
(!
exists
$dispatcher
->{
$method_name
}) {
warning(
"$base_url/#$method_name not found, pass()"
);
pass();
}
content_type
'application/json'
;
my
$response
;
my
$method_args
= request->body
? from_json(request->body)
:
undef
;
debug(
"[handle_restrpc_call($method_name)] "
,
$method_args
);
my
$start_request
=
time
();
my
Dancer::RPCPlugin::CallbackResult
$continue
=
eval
{
local
$Dancer::RPCPlugin::ROUTE_INFO
= {
plugin
=> PLUGIN_NAME,
endpoint
=>
$base_url
,
rpc_method
=>
$method_name
,
full_path
=> request->path,
http_method
=>
uc
(request->method),
};
$callback
?
$callback
->(request(),
$method_name
,
$method_args
)
: callback_success();
};
if
(
my
$error
= $@) {
my
$error_response
= Dancer::RPCPlugin::ErrorResponse->new(
error_code
=> 500,
error_message
=>
$error
,
);
status
$error_response
->return_status(PLUGIN_NAME);
$response
=
$error_response
->as_restrpc_error;
}
elsif
(!blessed(
$continue
) || !
$continue
->isa(
'Dancer::RPCPlugin::CallbackResult'
)) {
my
$error_response
= Dancer::RPCPlugin::ErrorResponse->new(
error_code
=> 500,
error_message
=>
"Internal error: 'callback_result' wrong class "
. blessed(
$continue
),
);
status
$error_response
->return_status(PLUGIN_NAME);
$response
=
$error_response
->as_restrpc_error;
}
elsif
(blessed(
$continue
) && !
$continue
->success) {
my
$error_response
= Dancer::RPCPlugin::ErrorResponse->new(
error_code
=>
$continue
->error_code,
error_message
=>
$continue
->error_message,
);
status
$error_response
->return_status(PLUGIN_NAME);
$response
=
$error_response
->as_restrpc_error;
}
else
{
my
Dancer::RPCPlugin::DispatchItem
$di
=
$dispatcher
->{
$method_name
};
my
$handler
=
$di
->code;
my
$package
=
$di
->
package
;
$response
=
eval
{
$code_wrapper
->(
$handler
,
$package
,
$method_name
,
$method_args
);
};
debug(
"[handled_restrpc_request($method_name)] "
, flatten_data(
$response
));
if
(
my
$error
= $@) {
my
$error_response
= blessed(
$error
) &&
$error
->can(
'as_restrpc_error'
)
?
$error
: error_response(
error_code
=> -32500,
error_message
=>
$error
,
error_data
=>
$method_args
,
);
status
$error_response
->return_status(PLUGIN_NAME);
$response
=
$error_response
->as_restrpc_error;
}
elsif
(blessed(
$response
) &&
$response
->can(
'as_restrpc_error'
)) {
$response
=
$response
->as_restrpc_error;
}
elsif
(blessed(
$response
)) {
$response
= flatten_data(
$response
);
}
}
$response
= {
result
=>
$response
}
if
!
ref
(
$response
);
my
$jsonise_options
= {
canonical
=> 1};
if
(config->{encoding} && config->{encoding} =~ m{^utf-?8$}i) {
$jsonise_options
->{utf8} = 1;
}
info(
sprintf
(
"[RPC::RESTRPC] request for %s took %.4fs"
,
$method_name
,
time
() -
$start_request
));
return
to_json(
$response
,
$jsonise_options
);
};
debug(
"setting routes (restrpc): $base_url "
,
$lister
);
for
my
$call
(
keys
%{
$dispatcher
}) {
my
$endpoint
=
"$base_url/$call"
;
post
$endpoint
,
$handle_call
;
}
};
sub
build_dispatcher_from_pod {
my
(
$pkgs
,
$endpoint
) =
@_
;
debug(
"[build_dispatcher_from_pod]"
);
return
dispatch_table_from_pod(
plugin
=>
'restrpc'
,
packages
=>
$pkgs
,
endpoint
=>
$endpoint
,
);
}
sub
build_dispatcher_from_config {
my
(
$config
,
$endpoint
) =
@_
;
debug(
"[build_dispatcher_from_config] "
);
return
dispatch_table_from_config(
plugin
=>
'restrpc'
,
config
=>
$config
,
endpoint
=>
$endpoint
,
);
}
register_plugin();
true;