AnyEvent-HTTPD-ExtDirect
view release on metacpan or search on metacpan
lib/AnyEvent/HTTPD/ExtDirect.pm view on Meta::CPAN
### PACKAGE GLOBAL VARIABLE ###
#
# Version of the module
#
our $VERSION = '3.20';
### PUBLIC CLASS METHOD (CONSTRUCTOR) ###
#
# Instantiate a new AnyEvent::HTTPD::ExtDirect object
#
sub new {
my $class = shift;
my %arg = @_ == 1 && 'HASH' eq ref $_[0] ? %{ $_[0] }
: @_
;
my $api = delete $arg{api} || RPC::ExtDirect->get_api();
my $config = delete $arg{config} || $api->config;
$config->add_accessors(
overwrite => 1,
complex => [{
accessor => 'router_class_anyevent',
fallback => 'router_class',
}, {
accessor => 'eventprovider_class_anyevent',
fallback => 'eventprovider_class',
}],
);
for my $var ( qw/ router_class eventprovider_class / ) {
my $method = "${var}_anyevent";
$config->$method( delete $arg{$var} ) if exists $arg{$var};
}
# AnyEvent::HTTPD wants only IP addresses
$arg{host} = '127.0.0.1' if $arg{host} =~ /localhost/io;
my $self = $class->SUPER::new(%arg);
$self->config($config);
$self->api($api);
return $self;
}
### PUBLIC INSTANCE METHOD ###
#
# Run the server
#
sub run {
my ($self) = @_;
my $config = $self->config;
$self->set_callbacks(
api_path => $config->api_path,
router_path => $config->router_path,
poll_path => $config->poll_path,
);
$self->SUPER::run();
}
### PUBLIC INSTANCE METHOD ###
#
# Handle Ext.Direct API calls
#
sub handle_api {
my ($self, $req) = @_;
# Get the API JavaScript chunk
my $js = eval {
$self->api->get_remoting_api( config => $self->config )
};
# If JS API call failed, return error
return $self->_error_response if $@;
# Content length should be in octets
my $content_length = do { use bytes; my $len = length $js };
$req->respond([
200,
'OK',
{
'Content-Type' => 'application/javascript',
'Content-Length' => $content_length,
},
$js
]);
$self->stop_request;
}
### PUBLIC INSTANCE METHOD ###
#
# Handle Ext.Direct method requests
#
sub handle_router {
my ($self, $req) = @_;
if ( $req->method ne 'POST' ) {
$req->respond( $self->_error_response );
$self->stop_request;
return;
}
my $config = $self->config;
my $api = $self->api;
# Naked AnyEvent::HTTPD::Request object doesn't provide several
# utility methods we'll need down below, and we will need it as
lib/AnyEvent/HTTPD/ExtDirect.pm view on Meta::CPAN
+{ @{ $result->[1] } },
$result->[2]->[0],
]);
$self->stop_request;
}
### PUBLIC INSTANCE METHOD ###
#
# Polls Event handlers for events, returning serialized stream
#
sub handle_events {
my ($self, $req) = @_;
# Only GET and POST methods are supported for polling
my $method = $req->method;
if ( $method ne 'GET' && $method ne 'POST' ) {
$req->respond( $self->_error_response );
$self->stop_request;
return;
}
my $config = $self->config;
my $api = $self->api;
my $env = bless $req, __PACKAGE__.'::Env';
my $provider_class = $config->eventprovider_class_anyevent;
eval "require $provider_class";
my $provider = $provider_class->new(
config => $config,
api => $api,
);
# Polling for Events is safe from exceptions
my $http_body = $provider->poll($env);
my $content_length
= do { no warnings 'void'; use bytes; length $http_body };
$req->respond([
200,
'OK',
{
'Content-Type' => 'application/json; charset=utf-8',
'Content-Length' => $content_length,
},
$http_body,
]);
$self->stop_request;
}
### PUBLIC INSTANCE METHOD ###
#
# Register the callbacks for Ext.Direct handlers.
# This effectively "primes" the server but does not make it
# enter a blocking wait.
#
sub set_callbacks {
my ($self, %arg) = @_;
my $config = $self->config;
my $api_path = $arg{api_path} || $config->api_path;
my $router_path = $arg{router_path} || $config->router_path;
my $poll_path = $arg{poll_path} || $config->poll_path;
$self->reg_cb(
$api_path => $self->can('handle_api'),
$router_path => $self->can('handle_router'),
$poll_path => $self->can('handle_events'),
);
}
### PUBLIC INSTANCE METHODS ###
#
# Read-write accessors.
#
RPC::ExtDirect::Util::Accessor::mk_accessors(
simple => [qw/ api config /],
);
############## PRIVATE METHODS BELOW ##############
### PRIVATE INSTANCE METHOD ###
#
# Deals with intricacies of POST-fu and returns something suitable to
# feed to Router (string or hashref, really). Or undef if something
# goes too wrong to recover.
#
# This code was mostly copied from the Plack gateway and adapted
# for AnyEvent::HTTPD.
#
sub _extract_post_data {
my ($self, $req) = @_;
# The smartest way to tell if a form was submitted that *I* know of
# is to look for 'extAction' and 'extMethod' keywords in form params.
my $is_form = $req->param('extAction') && $req->param('extMethod');
# If form is not involved, it's easy: just return raw POST (or undef)
if ( !$is_form ) {
my $postdata = $req->content;
return $postdata ne '' ? $postdata
: undef
;
};
# If any files are attached, extUpload field will be set to 'true'
my $has_uploads = $req->param('extUpload') eq 'true';
# Outgoing hash
my %keyword;
# Pluck all parameters from the Request object
for my $param ( $req->params ) {
my @values = $req->param($param);
( run in 2.289 seconds using v1.01-cache-2.11-cpan-5735350b133 )