The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Plack::Middleware;
use strict;
use warnings;
use Carp ();
use parent qw(Plack::Component);
use Plack::Util;
use Plack::Util::Accessor qw( app );

sub wrap {
    my($self, $app, @args) = @_;
    if (ref $self) {
        $self->{app} = $app;
    } else {
        $self = $self->new({ app => $app, @args });
    }
    return $self->to_app;
}

1;

__END__

=head1 NAME

Plack::Middleware - Base class for easy-to-use PSGI middleware

=head1 SYNOPSIS

  package Plack::Middleware::Foo;
  use parent qw( Plack::Middleware );

  sub call {
      my($self, $env) = @_;
      # Do something with $env

      # $self->app is the original app
      my $res = $self->app->($env);

      # Do something with $res
      return $res;
  }

  # then in app.psgi
  use Plack::Builder;

  my $app = sub { ... } # as usual

  builder {
      enable "Plack::Middleware::Foo";
      enable "Plack::Middleware::Bar", %options;
      $app;
  };

=head1 DESCRIPTION

Plack::Middleware is a utility base class to write PSGI
middleware. All you have to do is to inherit from Plack::Middleware
and then implement the callback C<call> method (or the C<to_app> method
that would return the PSGI code reference) to do the actual work. You
can use C<< $self->app >> to call the original (wrapped) application.

Your middleware object is created at the PSGI application compile time
and is persistent during the web server life cycle (unless it is a
non-persistent environment such as CGI), so you should never set or
cache per-request data like C<$env> in your middleware object. See
also L<Plack::Component/"OBJECT LIFECYCLE">.

See L<Plack::Builder> how to actually enable middleware in your
I<.psgi> application file using the DSL. If you do not like our
builder DSL, you can also use the C<wrap> method to wrap your application
with a middleware:

  use Plack::Middleware::Foo;

  my $app = sub { ... };
  $app = Plack::Middleware::Foo->wrap($app, %options);
  $app = Plack::Middleware::Bar->wrap($app, %options);

=head1 RESPONSE CALLBACK

The typical middleware is written like this:

  package Plack::Middleware::Something;
  use parent qw(Plack::Middleware);

  sub call {
      my($self, $env) = @_;
      # pre-processing $env
      my $res = $self->app->($env);
      # post-processing $res
      return $res;
  }

The tricky thing about post-processing the response is that it could
either be an immediate 3 element array ref, or a code reference that
implements the delayed (streaming) interface.

Dealing with these two types of response in each piece of middleware
is pointless, so you're recommended to use the C<response_cb> wrapper
function in L<Plack::Util> when implementing a post processing
middleware.

  sub call {
      my($self, $env) = @_;
      # pre-processing $env
      my $res = $app->($env);

      return Plack::Util::response_cb($res, sub {
          my $res = shift;
          # do something with $res;
      });
  }

The callback function gets a response as an array reference, and you can
update the reference to implement the post-processing. In the normal
case, this arrayref will have three elements (as described by the PSGI
spec), but will have only two elements when using a C<$writer> as
described below.

  package Plack::Middleware::Always500;
  use parent qw(Plack::Middleware);
  use Plack::Util;

  sub call {
      my($self, $env) = @_;
      my $res  = $self->app->($env);
      return Plack::Util::response_cb($res, sub {
          my $res = shift;
          $res->[0] = 500;
          return;
      });
  }

In this example, the callback gets the C<$res> and updates its first
element (status code) to 500. Using C<response_cb> makes sure that
this works with the delayed response too.

You're not required (and not recommended either) to return a new array
reference - they will be simply ignored. You're suggested to
explicitly return, unless you fiddle with the content filter callback
(see below).

Similarly, note that you have to keep the C<$res> reference when you
swap the entire response.

  Plack::Util::response_cb($res, sub {
      my $res = shift;
      $res = [ $new_status, $new_headers, $new_body ]; # THIS DOES NOT WORK
      return;
  });

This does not work, since assigning a new anonymous array to C<$res>
doesn't update the original PSGI response value. You should instead
do:

  Plack::Util::response_cb($res, sub {
      my $res = shift;
      @$res = ($new_status, $new_headers, $new_body); # THIS WORKS
      return;
  });

The third element of the response array ref is a body, and it could
be either an arrayref or L<IO::Handle>-ish object. The application could
also make use of the C<$writer> object if C<psgi.streaming> is in
effect, and in this case, the third element will not exist
(C<@$res == 2>). Dealing with these variants is again really painful,
and C<response_cb> can take care of that too, by allowing you to return
a content filter as a code reference.

  # replace all "Foo" in content body with "Bar"
  Plack::Util::response_cb($res, sub {
      my $res = shift;
      return sub {
          my $chunk = shift;
          return unless defined $chunk;
          $chunk =~ s/Foo/Bar/g;
          return $chunk;
      }
  });

The callback takes one argument C<$chunk> and your callback is
expected to return the updated chunk. If the given C<$chunk> is undef,
it means the stream has reached the end, so your callback should also
return undef, or return the final chunk and return undef when called
next time.

=head1 SEE ALSO

L<Plack> L<Plack::Builder> L<Plack::Component>

=cut