our
$VERSION
=
'1.09'
;
my
%dispatch_builder_map
= (
pod
=> \
&build_dispatcher_from_pod
,
config
=> \
&build_dispatcher_from_config
,
);
register PLUGIN_NAME ,=>
sub
{
my
(
$self
,
$endpoint
,
$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},
$endpoint
);
my
$lister
= Dancer::RPCPlugin::DispatchMethodList->new();
$lister
->set_partial(
protocol
=> PLUGIN_NAME,
endpoint
=>
$endpoint
,
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 jsonrpc-handler build: "
,
$lister
);
my
$handle_call
=
sub
{
my
(
$ct
) = (
split
/;\s*/, request->content_type, 2);
if
(
$ct
ne
'application/json'
) {
pass();
}
my
@requests
= unjson(request->body);
if
(!
exists
(
$requests
[0]->{jsonrpc}) or
$requests
[0]->{jsonrpc} ne
"2.0"
) {
pass();
}
debug(
"[handle_jsonrpc_request] Processing: "
, request->body);
content_type
'application/json'
;
my
@responses
;
for
my
$request
(
@requests
) {
my
$method_name
=
$request
->{method};
if
(!
exists
$dispatcher
->{
$method_name
}) {
push
(
@responses
,
jsonrpc_response(
$request
->{id},
error
=> {
code
=> -32601,
message
=>
"Method '$method_name' not found"
,
}
)
);
next
;
}
my
$method_args
=
$request
->{params};
my
$start_request
=
time
();
debug(
"[handle_jsonrpc_call($method_name)] "
,
$method_args
);
my
Dancer::RPCPlugin::CallbackResult
$continue
=
eval
{
local
$Dancer::RPCPlugin::ROUTE_INFO
= {
plugin
=> PLUGIN_NAME,
endpoint
=>
$endpoint
,
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
= $@) {
push
(
@responses
,
jsonrpc_response(
$request
->{id},
error
=> {
code
=> -32500,
message
=>
$error
,
}
)
);
next
;
}
if
(!blessed(
$continue
) || !
$continue
->isa(
'Dancer::RPCPlugin::CallbackResult'
)) {
push
@responses
, jsonrpc_response(
$request
->{id},
error
=> {
code
=> -32603,
message
=>
"Internal error: 'callback_result' wrong class "
. blessed(
$continue
),
}
);
next
;
}
elsif
(blessed(
$continue
) && !
$continue
->success) {
push
@responses
, jsonrpc_response(
$request
->{id},
error
=> {
code
=>
$continue
->error_code,
message
=>
$continue
->error_message,
}
);
next
;
}
my
Dancer::RPCPlugin::DispatchItem
$di
=
$dispatcher
->{
$method_name
};
my
$handler
=
$di
->code;
my
$package
=
$di
->
package
;
my
$result
=
eval
{
$code_wrapper
->(
$handler
,
$package
,
$method_name
,
$method_args
);
};
my
$error
= $@;
debug(
"[handled_jsonrpc_request($method_name)] "
, flatten_data(
$result
));
info(
sprintf
(
"[RPC::JSONRPC] request for %s took %.4fs"
,
$method_name
,
time
() -
$start_request
));
if
(
$error
) {
my
$error_response
= blessed(
$error
) &&
$error
->can(
'as_jsonrpc_error'
)
?
$error
: error_response(
error_code
=> -32500,
error_message
=>
$error
,
error_data
=>
$method_args
,
);
status
$error_response
->return_status(PLUGIN_NAME);
push
@responses
, jsonrpc_response(
$request
->{id},
%{
$error_response
->as_jsonrpc_error },
);
next
;
}
if
(blessed(
$result
) &&
$result
->can(
'as_jsonrpc_error'
)) {
push
@responses
, jsonrpc_response(
$request
->{id},
%{
$result
->as_jsonrpc_error }
);
next
;
}
elsif
(blessed(
$result
)) {
$result
= flatten_data(
$result
);
}
push
@responses
, jsonrpc_response(
$request
->{id},
result
=>
$result
);
}
my
$jsonise_options
= {
canonical
=> 1};
if
(config->{encoding} && config->{encoding} =~ m{^utf-?8$}i) {
$jsonise_options
->{utf8} = 1;
}
my
$response
;
if
(
@responses
== 1) {
$response
= to_json(
$responses
[0],
$jsonise_options
);
}
else
{
$response
= to_json([
grep
{
defined
(
$_
->{id})}
@responses
],
$jsonise_options
);
}
debug(
"[jsonrpc_response] "
,
$response
);
return
$response
;
};
debug(
"setting route (jsonrpc): $endpoint "
,
$lister
);
post
$endpoint
,
$handle_call
;
};
sub
unjson {
my
(
$body
) =
@_
;
my
@requests
;
my
$unjson
= from_json(
$body
, {
utf8
=> 1});
if
(
ref
(
$unjson
) ne
'ARRAY'
) {
@requests
= (
$unjson
);
}
else
{
@requests
=
@$unjson
;
}
return
@requests
;
}
sub
jsonrpc_response {
my
(
$id
,
$type
,
$data
) =
@_
;
return
{
jsonrpc
=>
'2.0'
,
id
=>
$id
,
$type
=>
$data
,
};
}
sub
build_dispatcher_from_pod {
my
(
$pkgs
,
$endpoint
) =
@_
;
debug(
"[build_dispatcher_from_pod]"
);
return
dispatch_table_from_pod(
plugin
=> PLUGIN_NAME,
packages
=>
$pkgs
,
endpoint
=>
$endpoint
,
);
}
sub
build_dispatcher_from_config {
my
(
$config
,
$endpoint
) =
@_
;
debug(
"[build_dispatcher_from_config] $endpoint"
);
return
dispatch_table_from_config(
plugin
=> PLUGIN_NAME,
config
=>
$config
,
endpoint
=>
$endpoint
,
);
}
register_plugin;
1;