Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Exploring Plack Middlewares

Exploring Plack Middlewares

My talk at German Perl Workshop 2013
Plack is Rack analogue for Perl programming language

7a9fca19d08180cd24d1177e6e22a6d2?s=128

Oleg Komarov

March 14, 2013
Tweet

Transcript

  1. Exploring Plack Middlewares Oleg Komarov 2013-03-15

  2. Section 1 Overview: PSGI & Plack

  3. PSGI Perl Web Server Gateway Interface https://metacpan.org/module/PSGI web servers PSGI

    server/application container provides environment applications mappings: environment -> response
  4. Benefits portability reusability/composability frameworks support Mojolicious::Plugin::MountPSGI $self->plugin("MountPSGI", {"/", => "ext/MyApp/app.psgi"}

    ); Catalyst: use MyApp; MyApp->setup_engine("PSGI"); my $app = sub {MyApp->run(@_)};
  5. Frameworks Figure: with Dancer

  6. Hello world sub { my $env = shift; return [

    200, ["Content-Type" => "text/plain"], ["Hello World"], ]; }
  7. $env An unblessed hashref CGI keys without period REQUEST METHOD

    SCRIPT NAME QUERY STRING . . . PSGI-specific keys psgi.version psgi.url scheme psgi.input psgi.errors psgi.multithread psgi.multiprocess psgi.run once psgi.nonblocking psgi.streaming
  8. Middleware package Plack::Middleware::Foo; use parent qw(Plack::Middleware); sub call { my

    ($self, $env) = @_; # pre-process $env my $response = $self->app->($env); # post-process $response return $response; } # or for delayed response Plack::Util::response_cb($response, sub { my $response = shift; ... })
  9. How to use it use Plack::Middleware::Foo; ... $app = Plack::Middleware::Foo->wrap($app,

    %options);
  10. Easier with Plack::Builder builder { enable "Foo", foo => 1,

    bar => 2; enable "Baz"; ... $app; };
  11. Access to %options package Plack::Middleware::Foo; use parent qw(Plack::Middleware); use Plack::Util::Accessor

    qw(foo bar); sub call { ... $self->foo $self->bar ... }
  12. Prepare sub prepare_app { my $self = shift; die "it’s

    boring" unless $self->fun; }
  13. Top-level logic builder { if ($location eq "Rome") { enable

    "Behaviour::Roman"; } ... };
  14. Top-level logic builder { ... enable_if {$visitor_count++ == 1E6} "ShowBanner";

    ... };
  15. Top-level logic builder { enable_if {$_[0]->{QUERY_STRING} =~ /&debug=1/} "Debug" ...

    };
  16. Top-level logic builder { enable "Foo", cb => \&a_cunning_plan; ...

    };
  17. Section 2 Examples

  18. Replacing parts of Apache Access Auth:: ErrorDocument Rewrite SSI

  19. And even more Cache Header logging profiling SizeLimit Throttle

  20. Developer tools Debug Delay InteractiveDebugger REPL

  21. Developer tools

  22. Aspect (lack of) Figure: use Aspect!

  23. unless is production() unless (is_production()) { enable "DevTools"; }

  24. DevTools with Aspect package Plack::Middleware::DevTools; use parent qw(Plack::Middleware); use Aspect

    (); sub call { my ($self, $env) = @_; my @hooks; ... # hooks ... my $response = $self->app->($env); # extract data from $env undef $env; return $response; }
  25. Tracing hook $env->{"devtools.http"} = []; push @hooks, Aspect::before { my

    (undef, $url) = $_->args; push @{$env->{"devtools.http"}}, $url; } Aspect::call qr/^LWP::UserAgent::(?:get|post)$/;
  26. Faking hook push @hooks, Aspect::after { my @args = $_->args;

    my $true_result = $_->return_value; my $fake_result = get_fake_result($true_result, @args); $_->return_value($fake_result); } Aspect::call "API::method" & Aspect::returning;
  27. Find missing translations push @hooks, Aspect::after { if ($cur_lang ne

    $default_lang) { my $msgid = [$_->args]->[0]; my $msgstr = [$_->return_value]->[0]; if ($msgid eq $msgstr) { # replace into db } else { # delete from db } } } Aspect::call qr/::gettext$/;
  28. Section 3 Refactoring existing code

  29. Dependencies Plack::Builder might have provided information about outer middlewares (but

    it doesn’t) Communication via $env P::M::MyAuth sets $env->{"myauth.user id"} P::M::MyUser fetches user data from DB using $env->{"myauth.user id"} and puts it in $env->{"myuser.user"}
  30. Special-purpose middlewares E.g. P::M::MyAuth and P::M::MyUser Captcha FakeAuth MailFatals Reqid

  31. Thank you Questions? https://github.com/komarov