Catalyst-Runtime

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

5.90126 - 2020-01-19
  - fix for broken distribution

5.90125 - 2020-01-18
  - Support samesite flag for cookies (mitchjacksontech++)
  - utility method on Catalyst::Action 'equals'
  - new predicate methods 'has_request' and 'has_response'. Useful in
    plugins that might run bits before a request is finalized.

5.90124 - 2019-01-18
  - Fix problem with from_psgi_response and streaming applications (
    https://github.com/perl-catalyst/catalyst-runtime/pull/168).

5.90123 - 2018-11-27
  - Fix emitting warnings when REMOTE_ADDR is undefined (RT#113388)
  - Fix $c->req->hostname empty for IPv6 clients (RT#75731)
  - split code to log stats report into a separate log_stats method (RT#127392)

5.90122 - 2018-11-03
  - releasing as stable

Changes  view on Meta::CPAN

5.90059_003 - 2013-12-24
  - More documentation about alternative ways to setup middleware.
  - removed unneeded use of Devel::Dwarn in test case that was causing
    fails to install (sorry).
  - When finalizing caught errors, if the error conforms to the interface as
    described by Plack::Middleware::HTTPExceptions, rethrow it and let the
    middleware deal with it.

5.90059_002 - 2013-12-21
  - We now pass a scalar or filehandle directly to you Plack handler, rather
    than always use the streaming interface (we are still always using a
    delayed response callback).  This means that you can make use of Plack
    middleware like Plack::Middleware::XSendfile and we expect better use of
    server features (when they exist) like correct use of chunked encoding or
    properly non blocking streaming when running under a supporting server like
    Twiggy.  See Catalyst::Delta for more.  This change might cause issues if
    you are making heaving use of streaming (although in general we expect things
    to work much better.
  - In the case when we remove a content body from the response because you set
    an information status or a no content type status, warn that we are doing so
    when in debug mode.  You might see additional debugging information to help
    you find and remove unneeded response bodies.
  - Updated the code where Catalyst tries to guess a content length when you
    fail to provide one.  This should cause less issues when trying to guess the
    length of a funky filehandle.  This now uses Plack::Middleware::ContentLength
  - Removed custom code to remove body content when the request is HEAD and
    swapped it for Plack::Middleware::Head

Changes  view on Meta::CPAN

  - In Catalyst::Test, don't mangle headers of non-HTML responses. RT#79043

5.90008 - TRIAL 2012-02-06 20:49:00

 New features and refactoring:
  - Much of the Catalyst::Engine code has been moved into Catalyst::Request
    and Catalyst::Response, to be able to better support asynchronous web
    servers such as Twiggy, by making the application engine more reenterant.

    This change is as a prequel to full asynchronous support inside Catalyst
    for AnyEvent and IO::Async backends, which allow highly scaleable streaming
    (for applications such as multi-part XML HTTPRequests, and Websockets).

 Deprecations:
  - This means that the $c->engine->env method to access the PSGI environment
    is now deprecated. The accessor for the PSGI env is now on Catalyst::Request
    as per applications which were using Catalyst::Engine::PSGI

    Catalyst::Engine::PSGI is now considered fully deprecated.

  - The private _dump method in Catalyst::Log is now deprecated. The dumper is

Changes  view on Meta::CPAN

        - Fix return value of $c->req->body, which delegates to the body
          method on the requests HTTP::Body instance
          - Test for this
        - Fix calling $c->req->body from inside an overridden prepare_action
          method in a plugin, as used by Catalyst::Plugin::Server
          - Test for this
        - Fix assignment to Catalyst::Dispatcher's preload_dispatch_types and
          postload_dispatch_types attributes - assigning a list should later
          return a listref. Fixes Catalyst::Plugin::Server.
          - Tests for this
        - Change streaming test to serve itself rather than 01use.t, making
          test sync for engines easier
        - Refactor capturing of $app from Catalyst::Controller into
          Catalyst::Component::ApplicationAttribute for easier reuse in other
          components
        - Make the test suites YAML dependency optional
        - Make debug output show class name for the engine and dispatcher
          rather than the stringified ref.
        - Make MyApp immutable at the end of the scope after the setup
          method is called, fixing issues with plugins which have their
          own new methods by inlining a constructor on MyApp

Changes  view on Meta::CPAN

        - Fixed relative forwarding
        - Fixed forward arrows in debug output

5.49_04 2005-11-09 23:00:00
        - Made context, dispatcher, engine, request and response classes
          configurable.
        - Added $c->stack.
        - Fixed dispatcher to ignore unknown attributes.
        - Improved format of startup debug log.
        - Updated built in server to restart on win32. (Will Hawes)
        - Fixed streaming write from a filehandle to stop writing
          if the browser is closed.
        - Added $c->controller, $c->model and $c->view shortcuts.
        - Switched to Text::SimpleTable.

5.49_03 2005-11-03 12:00:00
        - Fixed $c->req->{path} for backwards-compatibility.
        - Allow debug to be disabled via ENV as well as enabled.
        - Added -scripts option to catalyst.pl for script updating
        - Changed helpers to default to long types, Controller instead of C
        - Added Catalyst::Controller, Catalyst::Model and Catalyst::View

Changes  view on Meta::CPAN

        - Added an 'abort' method to the Log api, so that you can
          kill loggging for a whole request.
        - Added $c->uri_for method to simplify url handling.
        - Added more tests and reorganized the t directory.
        - Reimplemented core engines, all are now CGI based for better test
          coverage and maintainability.
        - Added fork support to built in test server.
        - Fixed all memory leaks.
        - Thread-related bug fixes and tests.  We now believe the Catalyst
          core to be thread-safe.
        - Added streaming IO support through $c->req->read() and
          $c->res->write()
        - Added MyApp->config->{parse_on_demand} (streaming input)
        - Added $c->req->handle and $c->res->handle
        - Improved documentation
        - Fixed mkpath in Catalyst::Helper (Autrijus Tang)
        - Fixed bug in dispatcher where an invalid path could call a valid
          action. (Andy Grundman)
        - Fixed Helper so it works with CRLF line-endings. (Andy Grundman)

5.33  2005-08-10 15:25:00
        - Now with updated manifest.

MANIFEST  view on Meta::CPAN

t/aggregate/live_component_controller_action_global.t
t/aggregate/live_component_controller_action_go.t
t/aggregate/live_component_controller_action_index.t
t/aggregate/live_component_controller_action_index_or_default.t
t/aggregate/live_component_controller_action_inheritance.t
t/aggregate/live_component_controller_action_local.t
t/aggregate/live_component_controller_action_multipath.t
t/aggregate/live_component_controller_action_path.t
t/aggregate/live_component_controller_action_path_matchsingle.t
t/aggregate/live_component_controller_action_private.t
t/aggregate/live_component_controller_action_streaming.t
t/aggregate/live_component_controller_action_visit.t
t/aggregate/live_component_controller_actionroles.t
t/aggregate/live_component_controller_anon.t
t/aggregate/live_component_controller_args.t
t/aggregate/live_component_controller_attributes.t
t/aggregate/live_component_controller_httpmethods.t
t/aggregate/live_component_controller_moose.t
t/aggregate/live_component_view_single.t
t/aggregate/live_engine_request_auth.t
t/aggregate/live_engine_request_body.t

lib/Catalyst.pm  view on Meta::CPAN

    $response->finalized_headers(1);
}

=head2 $c->finalize_encoding

Make sure your body is encoded properly IF you set an encoding.  By
default the encoding is UTF-8 but you can disable it by explicitly setting the
encoding configuration value to undef.

We can only encode when the body is a scalar.  Methods for encoding via the
streaming interfaces (such as C<write> and C<write_fh> on L<Catalyst::Response>
are available).

See L</ENCODING>.

=cut

sub finalize_encoding {
    my $c = shift;
    my $res = $c->res || return;

lib/Catalyst/Delta.pod  view on Meta::CPAN

be converting all uses of L<Class::Load> to L<Module::Runtime>.  We will
also convert L<Catalyst::Utils\ensure_class_loaded> to be based on
L<Module::Runtime> to allow some time for you to update code, however at
some future point this method will be removed so you should stop
using it now.

=head3 Support passing Body filehandles directly to your Plack server.

We changed the way we return body content (from response) to whatever
Plack handler you are using (Starman, FastCGI, etc.)  We no longer
always use the streaming interface for the cases when the body is a
simple scalar, object or filehandle like.  In those cases we now just
pass the simple response on to the plack handler.  This might lead to
some minor differences in how streaming is handled.  For example, you
might notice that streaming starts properly supporting chunked encoding when
on a server that supports that, or that previously missing headers
(possible content-length) might appear suddenly correct.  Also, if you
are using middleware like L<Plack::Middleware::XSendfile> and are using
a filehandle that sets a readable path, your server might now correctly
handle the file (rather than as before where Catalyst would stream it
very likely very slowly).

In other words, some things might be meaninglessly different and some
things that were broken codewise but worked because of Catalyst being
incorrect might suddenly be really broken.  The behavior is now more
correct in that Catalyst plays better with features that Plack offers
but if you are making heavy use of the streaming interface there could
be some differences so you should test carefully (this is probably not
the vast majority of people).  In particular if you are developing
using one server but deploying using a different one, differences in
what those server do with streaming should be noted.

Please see note below about changes to filehandle support and existing
Plack middleware to aid in backwards compatibility.

=head3 Distinguish between body null versus undef.

We also now more carefully distinguish the different between a body set
to '' and a body that is undef.  This might lead to situations where
again you'll get a content-length were you didn't get one before or
where a supporting server will start chunking output.  If this is an

lib/Catalyst/Delta.pod  view on Meta::CPAN

Code has been maintained here for backwards compatibility reasons.  This is no
longer supported and will be removed in upcoming release, so you should update
your code and / or upgrade to a newer version of L<Catalyst>

=head3 Deprecate setting Response->body after using write/write_fh

Setting $c->res->body to a filehandle after using $c->res->write or
$c->res->write_fh is no longer considered allowed, since we can't send
the filehandle to the underlying Plack handler.  For now we will continue
to support setting body to a simple value since this is possible, but at
some future release a choice to use streaming indicates that you will do
so for the rest of the request.


=head2 VERSION 5.90053

We are now clarifying the behavior of log, plugins and configuration during
the setup phase.  Since Plugins might require a log during setup, setup_log
must run BEFORE setup_plugins.   This has the unfortunate side effect that
anyone using the popular ConfigLoader plugin will not be able to supply
configuration to custom logs since the configuration is not yet finalized

lib/Catalyst/Engine.pm  view on Meta::CPAN

=head1 DESCRIPTION

=head1 METHODS


=head2 $self->finalize_body($c)

Finalize body.  Prints the response output as blocking stream if it looks like
a filehandle, otherwise write it out all in one go.  If there is no body in
the response, we assume you are handling it 'manually', such as for nonblocking
style or asynchronous streaming responses.  You do this by calling L</write>
several times (which sends HTTP headers if needed) or you close over
C<< $response->write_fh >>.

See L<Catalyst::Response/write> and L<Catalyst::Response/write_fh> for more.

=cut

sub finalize_body {
    my ( $self, $c ) = @_;
    my $res = $c->response; # We use this all over

lib/Catalyst/Engine.pm  view on Meta::CPAN

        ## someone calling ->write to presend some stuff, and then doing the rest
        ## via ->body, like in a template.

        ## We'll just use the old, existing code for this (or most of it)

        if(my $body = $res->body) {

          if ( blessed($body) && $body->can('read') or ref($body) eq 'GLOB' ) {

              ## In this case we have no choice and will fall back on the old
              ## manual streaming stuff.  Not optimal.  This is deprecated as of 5.900560+

              my $got;
              do {
                  $got = read $body, my ($buffer), $CHUNKSIZE;
                  $got = 0 unless $self->write($c, $buffer );
              } while $got > 0;

              close $body;
          }
          else {

lib/Catalyst/Response.pm  view on Meta::CPAN

        });
     } else {
        die "You can't set a Catalyst response from that, expect a valid PSGI response";
    }

    # Encoding compatibilty.   If the response set a charset, well... we need
    # to assume its properly encoded and NOT encode for this response.  Otherwise
    # We risk double encoding.

    # We check first to make sure headers have not been finalized.  Headers might be finalized
    # in the case where a PSGI response is streaming and the PSGI application already wrote
    # to the output stream and close the filehandle.
    if(!$self->finalized_headers && $self->content_type_charset) {
      # We have to do this since for backcompat reasons having a charset doesn't always
      # mean that the body is already encoded :(
      $self->_context->clear_encoding;
    }
}

=head1 NAME

lib/Catalyst/Response.pm  view on Meta::CPAN

    $c->response->body('Catalyst rocks!');

Sets or returns the output (text or binary data). If you are returning a large body,
you might want to use a L<IO::Handle> type of object (Something that implements the getline method
in the same fashion), or a filehandle GLOB. These will be passed down to the PSGI
handler you are using and might be optimized using server specific abilities (for
example L<Twiggy> will attempt to server a real local file in a non blocking manner).

If you are using a filehandle as the body response you are responsible for
making sure it conforms to the L<PSGI> specification with regards to content
encoding.  Unlike with scalar body values or when using the streaming interfaces
we currently do not attempt to normalize and encode your filehandle.  In general
this means you should be sure to be sending bytes not UTF8 decoded multibyte
characters.

Most of the time when you do:

    open(my $fh, '<:raw', $path);

You should be fine.  If you open a filehandle with a L<PerlIO> layer you probably
are not fine.  You can usually fix this by explicitly using binmode to set

lib/Catalyst/Response.pm  view on Meta::CPAN


=head2 $res->finalize_headers()

Writes headers to response if not already written

=head2 from_psgi_response

Given a PSGI response (either three element ARRAY reference OR coderef expecting
a $responder) set the response from it.

Properly supports streaming and delayed response and / or async IO if running
under an expected event loop.

If passed an object, will expect that object to do a method C<as_psgi>.

Example:

    package MyApp::Web::Controller::Test;

    use base 'Catalyst::Controller';
    use Plack::App::Directory;


    my $app = Plack::App::Directory->new({ root => "/path/to/htdocs" })
      ->to_app;

    sub myaction :Local Args {
      my ($self, $c) = @_;
      $c->res->from_psgi_response($app->($c->req->env));
    }

    sub streaming_body :Local {
      my ($self, $c) = @_;
      my $psgi_app = sub {
          my $respond = shift;
          my $writer = $respond->([200,["Content-Type" => "text/plain"]]);
          $writer->write("body");
          $writer->close;
      };
      $c->res->from_psgi_response($psgi_app);
    }

Please note this does not attempt to map or nest your PSGI application under
the Controller and Action namespace or path. You may wish to review 'PSGI Helpers'
under L<Catalyst::Utils> for help in properly nesting applications.

B<NOTE> If your external PSGI application returns a response that has a character
set associated with the content type (such as "text/html; charset=UTF-8") we set
$c->clear_encoding to remove any additional content type encoding processing later
in the application (this is done to avoid double encoding issues).

B<NOTE> If your external PSGI application is streaming, we assume you completely
handle the entire jobs (including closing the stream).  This will also bypass
the output finalization methods on Catalyst (such as 'finalize_body' which gets
called but then skipped when it finds that output is already finished.)  Its possible
this might cause issue with some plugins that want to do 'things' during those
finalization methods.  Just understand what is happening.

=head2 encodable_content_type

This is a regular expression used to determine of the current content type
should be considered encodable.  Currently we apply default encoding (usually

lib/Catalyst/Response.pm  view on Meta::CPAN


=head2 encodable_response

Given a L<Catalyst::Response> return true if its one that can be encoded.

     make sure there is an encoding set on the response
     make sure the content type is encodable
     make sure no content type charset has been already set to something different from the global encoding
     make sure no content encoding is present.

Note this does not inspect a body since we do allow automatic encoding on streaming
type responses.

=cut

sub encodable_response {
  my ($self) = @_;
  return 0 unless $self->_context; # Cases like returning a HTTP Exception response you don't have a context here...
  return 0 unless $self->_context->encoding;

  # The response is considered to have a 'manual charset' when a charset is already set on

lib/Catalyst/UTF8.pod  view on Meta::CPAN

This will use the alternative encoding for a single response.

B<NOTE> If you manually set the content-type character set to whatever $c->encoding->mime_name
is set to, we STILL encode, rather than assume your manual setting is a flag to override.  This
is done to support backward compatible assumptions (in particular L<Catalyst::View::TT> has set
a utf-8 character set in its default content-type for ages, even though it does not itself do any
encoding on the body response).  If you are going to handle encoding manually you may set
$c->clear_encoding for a single request response cycle, or as in the above example set an alternative
encoding.

=head2 Encoding with streaming type responses

L<Catalyst> offers two approaches to streaming your body response.  Again, you must remember
to set your content type prior to streaming, since invoking a streaming response will automatically
finalize and send your HTTP headers (and your content type MUST be one that matches the regular
expression given above.)

Also, if you are going to override $c->encoding (or invoke $c->clear_encoding), you should do
that before anything else!

The first streaming method is to use the C<write> method on the response object.  This method
allows 'inlined' streaming and is generally used with blocking style servers.

    sub stream_write :Local {
        my ($self, $c) = @_;
        $c->response->content_type('text/html');
        $c->response->write("<p>This is stream_write action ♥</p>");
    }

You may call the C<write> method as often as you need to finish streaming all your content.
L<Catalyst> will encode each line in turn as long as the content-type meets the 'encodable types'
requirement and $c->encoding is set (which it is, as long as you did not change it).

B<NOTE> If you try to change the encoding after you start the stream, this will invoke an error
response.  However since you've already started streaming this will not show up as an HTTP error
status code, but rather error information in your body response and an error in your logs.

B<NOTE> If you use ->body AFTER using ->write (for example you may do this to write your HTML
HEAD information as fast as possible) we expect the contents to body to be encoded as it
normally would be if you never called ->write.  In general unless you are doing weird custom
stuff with encoding this is likely to just already do the correct thing.

The second way to stream a response is to get the response writer object and invoke methods
on that directly:

lib/Catalyst/UTF8.pod  view on Meta::CPAN


L<http://catalyst.perl.org/calendar/2013/10>, L<http://catalyst.perl.org/calendar/2013/11>,
L<http://catalyst.perl.org/calendar/2013/12>, L<http://catalyst.perl.org/calendar/2013/13>,
L<http://catalyst.perl.org/calendar/2013/14>.

The main difference this year is that previously calling ->write_fh would return the actual
L<Plack> writer object that was supplied by your Plack application handler, whereas now we wrap
that object in a lightweight decorator object that proxies the C<write> and C<close> methods
and supplies an additional C<write_encoded> method.  C<write_encoded> does the exact same thing
as C<write> except that it will first encode the string when necessary.  In general if you are
streaming encodable content such as HTML this is the method to use.  If you are streaming
binary content, you should just use the C<write> method (although if the content type is set
correctly we would skip encoding anyway, but you may as well avoid the extra noop overhead).

The last style of content response that L<Catalyst> supports is setting the body to a filehandle
like object.  In this case the object is passed down to the Plack application handler directly
and currently we do nothing to set encoding.

    sub stream_body_fh :Local {
        my ($self, $c) = @_;
        my $path = File::Spec->catfile('t', 'utf8.txt');

lib/Catalyst/Upgrading.pod  view on Meta::CPAN

a few releases (trying to clean up the codebase after all).

If you have trouble with any of this, please bring it to the attention of the
Catalyst maintainer group.

=head2 basic async and event loop support

This version of L<Catalyst> offers some support for using L<AnyEvent> and
L<IO::Async> event loops in your application.  These changes should work
fine for most applications however if you are already trying to perform
some streaming, minor changes in this area of the code might affect your
functionality.  Please see L<Catalyst::Response\write_fh> for more and for a
basic example.

We consider this feature experimental.  We will try not to break it, but we
reserve the right to make necessary changes to fix major issues that people
run into when the use this functionality in the wild.

=head1 Upgrading to Catalyst 5.90030

=head2 Regex dispatch type is deprecated.

t/aggregate/live_component_controller_action_streaming.t  view on Meta::CPAN

    require Benchmark;
    Benchmark::timethis( $iters, \&run_tests );
}
else {
    for ( 1 .. $iters ) {
        run_tests();
    }
}

sub run_tests {
    # test direct streaming
    {
        ok( my $response = request('http://localhost/streaming'), 'Request' );
        ok( $response->is_success, 'Response Successful 2xx' );
        is( $response->content_type, 'text/plain', 'Response Content-Type' );
        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
        is( $response->header('X-Test-Header-Call-Count'), 1);

        SKIP:
        {
            if ( $ENV{CATALYST_SERVER} ) {
                skip "Using remote server", 1;
            }

            ok(!defined $response->content_length, 'No Content-Length for streaming responses');
            is(length $response->content, 12, 'Response content' );
        }

        is( $response->content,, <<'EOF', 'Content is a stream' );
foo
bar
baz
EOF
    }

    # test streaming by passing a handle to $c->res->body
  SKIP:
    {
        if ( $ENV{CATALYST_SERVER} ) {
            skip "Using remote server", 10;
        }

        my $file = "$FindBin::Bin/../lib/TestApp/Controller/Action/Streaming.pm";
        my $fh = IO::File->new( $file, 'r' );
        my $buffer;
        if ( defined $fh ) {
            $fh->read( $buffer, 2048 );
            $fh->close;
        }

        ok( my $response = request('http://localhost/action/streaming/body'),
            'Request' );
        ok( $response->is_success, 'Response Successful 2xx' );
        is( $response->content_type, 'text/plain', 'Response Content-Type' );
        is( $response->content_length, -s $file, 'Response Content-Length' );
        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
        is( $response->header('X-Test-Header-Call-Count'), 1);
        is( $response->content, $buffer, 'Content is read from filehandle' );

        ok( $response = request('http://localhost/action/streaming/body_glob'),
            'Request' );
        ok( $response->is_success, 'Response Successful 2xx' );
        is( $response->content_type, 'text/plain', 'Response Content-Type' );
        is( $response->content_length, -s $file, 'Response Content-Length' );
        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
        is( $response->header('X-Test-Header-Call-Count'), 1);
        is( $response->content, $buffer, 'Content is read from filehandle' );
    }

    {
        my $size = 128 * 1024; # more than one read with the default chunksize

        ok( my $response = request('http://localhost/action/streaming/body_large'), 'Request' );
        ok( $response->is_success, 'Response Successful 2xx' );
        is( $response->content_type, 'text/plain', 'Response Content-Type' );
        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
        is( $response->header('X-Test-Header-Call-Count'), 1);
        is( $response->content_length, $size, 'Response Content-Length' );
        is( $response->content, "\0" x $size, 'Content is read from filehandle' );
    }
}

done_testing;

t/lib/TestApp/Controller/Action/Streaming.pm  view on Meta::CPAN

package TestApp::Controller::Action::Streaming;

use strict;
use base 'TestApp::Controller::Action';

sub streaming : Global {
    my ( $self, $c ) = @_;
    for my $line ( split "\n", <<'EOF' ) {
foo
bar
baz
EOF
        $c->res->write("$line\n");
    }
}

t/optional_stress.json  view on Meta::CPAN

      "http://localhost/action/private/one",
      "http://localhost/action/private/two",
      "http://localhost/three",
      "http://localhost/action/private/four",
      "http://localhost/action/private/five"
   ],
   "component/controller/action/regexp" : [
      "http://localhost/action/regexp/10/hello",
      "http://localhost/action/regexp/hello/10"
   ],
   "component/controller/action/streaming" : [
      "http://localhost/streaming",
      "http://localhost/action/streaming/body"
   ],
   "engine/request/body" : [],
   "engine/request/cookies" : [],
   "engine/request/headers" : [],
   "engine/request/parameters" : [],
   "engine/request/uploads" : [],
   "engine/request/uri" : [
      "http://localhost/engine/request/uri/change_path",
      "http://localhost/engine/request/uri/change_base",
      "http://localhost/engine/request/uri",

t/psgi_utils.t  view on Meta::CPAN

    my $path = File::Spec->catfile('t', 'utf8.txt');
    open(my $fh, '<', $path) || die "trouble: $!";
    $c->res->from_psgi_response([200, ['Content-Type'=>'text/html'], $fh]);
  }

  sub direct :Local {
    my ($self, $c, $arg) = @_;
    $c->res->from_psgi_response([200, ['Content-Type'=>'text/html'], ["hello","world"]]);
  }

  sub streaming_body :Local {
    my ($self, $c) = @_;
    my $psgi_app = sub {
        my $respond = shift;
        my $writer = $respond->([200,["Content-Type" => "text/plain"]]);
        $writer->write("body");
        $writer->close;
    };
    $c->res->from_psgi_response($psgi_app);
  }
  sub streaming_body_with_charset :Local {
    my ($self, $c) = @_;
    my $psgi_app = sub {
        my $respond = shift;
        my $writer = $respond->([200,["Content-Type" => "text/plain; charset=utf-8"]]);
        $writer->write("body");
        $writer->close;
    };
    #$c->clear_encoding;
    $c->res->from_psgi_response($psgi_app);
  }

t/psgi_utils.t  view on Meta::CPAN

  my ($res, $c) = ctx_request('/docs/filehandle');
  is Encode::decode_utf8($res->content), "<p>This is stream_body_fh action ♥</p>\n";
}

{
  my ($res, $c) = ctx_request('/docs/direct');
  is $res->content, "helloworld";
}

{
  my ($res, $c) = ctx_request('/docs/streaming_body');
  is $res->content, "body";
}
{
  my ($res, $c) = ctx_request('/docs/streaming_body_with_charset');
  is $res->content, "body";
}

done_testing();



( run in 0.335 second using v1.01-cache-2.11-cpan-4d50c553e7e )