Alice

 view release on metacpan or  search on metacpan

lib/Alice/HTTP/Server.pm  view on Meta::CPAN

package Alice::HTTP::Server;

use AnyEvent;
use AnyEvent::HTTP;

use Fliggy::Server;
use Plack::Builder;
use Plack::Middleware::Static;
use Plack::Session::Store::File;
use Plack::Session::State::Cookie;

use Alice::HTTP::Request;
use Alice::HTTP::Stream::XHR;
use Alice::HTTP::Stream::WebSocket;

use JSON;
use Encode;
use Any::Moose;

has app => (
  is  => 'ro',
  isa => 'Alice',
  required => 1,
);

has httpd => (
  is  => 'rw',
  lazy => 1,
  builder => "_build_httpd",
);

has ping => (
  is  => 'rw',
  lazy => 1,
  default => sub {
    my $self = shift;
    AE::timer 1, 5, sub {
      $self->app->ping;
    };
  },
);

has port => (
  is => 'ro',
  default => 8080,
);

has address => (
  is => 'ro',
  default => "127.0.0.1",
);

has session => (
  is => 'ro'
);

has assets => (
  is => 'ro',
  required => 1,
);

my $url_handlers = [
  [ "say"          => "handle_message" ],
  [ "stream"       => "setup_xhr_stream" ],
  [ "wsstream"     => "setup_ws_stream" ],
  [ ""             => "send_index" ],
  [ "safe"         => "send_safe_index" ],
  [ "tabs"         => "tab_order" ],
  [ "savetabsets"  => "save_tabsets" ],
  [ "serverconfig" => "server_config" ],
  [ "save"         => "save_config" ],
  [ "login"        => "login" ],
  [ "logout"       => "logout" ],
  [ "export"       => "export_config" ],
];

sub url_handlers { return $url_handlers }

sub BUILD {
  my $self = shift;
  $self->httpd;
  $self->ping;
}

sub _build_httpd {
  my $self = shift;
  my $httpd;

  # eval in case server can't bind port
  eval {
    $httpd = Fliggy::Server->new(
      host => $self->address,
      port => $self->port,
    );
    $httpd->register_service(
      builder {
        enable "Session",
          store => $self->session,
          state => Plack::Session::State::Cookie->new(expires => 60 * 60 * 24 * 7);
        enable "Static", path => qr{^/static/}, root => $self->assets;
        enable "+Alice::HTTP::WebSocket";
        sub {
          my $env = shift;
          return sub {$self->dispatch($env, shift)}
        }
      }
    );
  };

  AE::log(warn => $@) if $@;
  return $httpd;
}

sub dispatch {
  my ($self, $env, $cb) = @_;

  my $req = Alice::HTTP::Request->new($env, $cb);
  my $res = $req->new_response(200);

  AE::log trace => $req->path;

  if ($self->auth_enabled) {
    unless ($req->path eq "/login" or $self->is_logged_in($req)) {
      $self->auth_failed($req, $res);
      return;
    }
  }
  for my $handler (@{$self->url_handlers}) {
    my $path = $handler->[0];
    if ($req->path_info =~ /^\/$path\/?$/) {
      my $method = $handler->[1];
      $self->$method($req, $res);
      return;
    }
  }
  $self->template($req, $res);
}

sub auth_failed {
  my ($self, $req, $res) = @_;

  if ($req->path =~ m{^(/(?:safe)?)$}) {
    $res->redirect("/login".($1 ? "?dest=$1" : ""));
    $res->body("bai");
  } else {
    $res->status(401);
    $res->body("unauthorized");
  }
  $res->send;
}

sub is_logged_in {
  my ($self, $req) = @_;
  my $session = $req->env->{"psgix.session"};
  return $session->{is_logged_in};
}

sub login {
  my ($self, $req, $res) = @_;

  my $dest = $req->param("dest") || "/";

  # no auth is required
  if (!$self->auth_enabled) {
    $res->redirect($dest);
    $res->send;
  }

  # we have credentials
  elsif (my $user = $req->param('username')
     and my $pass = $req->param('password')) {

    $self->authenticate($user, $pass, sub {
      my $app = shift;
      if ($app) {
        $req->env->{"psgix.session"} = {
          is_logged_in => 1,



( run in 1.112 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )