Prancer

 view release on metacpan or  search on metacpan

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


        $self->cookies->add($key => [@{$self->cookies->get_all($key) || []}, $value]);
    }

    return;
}

sub cookies {
    my $self = shift;
    return $self->{'_cookies'};
}

sub body {
    my $self = shift;

    # make the response be a callback
    if (ref($_[0]) && ref($_[0]) eq "CODE") {
        $self->{'_callback'} = shift;
        return;
    }

    # just add this to the body, whatever it is
    return $self->{'_response'}->body(@_);
}

sub finalize {
    my ($self, $status) = @_;
    $self->{'_response'}->status($status);

    # build the headers using something normal and then add them to the
    # response later. for whatever reason plack is being weird about this when
    # the same header name is being used more than once. though, i might be
    # doing it wrong.
    my $headers = HTTP::Headers::Fast->new();

    # add normal headers
    for my $key (keys %{$self->headers()}) {
        for my $value (@{$self->headers->get_all($key)}) {
            $headers->push_header($key => $value);
        }
    }

    # add cookies
    for my $key (keys %{$self->cookies()}) {
        for my $value (@{$self->cookies->get_all($key)}) {
            $headers->push_header("Set-Cookie" => $self->_bake_cookie($key, $value));
        }
    }

    # now add the headers we've compiled
    $self->{'_response'}->headers($headers);

    if (ref($self->{'_callback'}) &&
        ref($self->{'_callback'}) eq "CODE") {

        # the extra array ref brackets around the sub are because Web::Simple,
        # which we use as the router, will not do a callback without them. by
        # returning an array ref we are telling Web::Simple that we are giving
        # it a PSGI response. from the Web::Simple docs:
        #
        #     Well, a sub is a valid PSGI response too (for ultimate streaming
        #     and async cleverness). If you want to return a PSGI sub you have
        #     to wrap it into an array ref.
        #
        return [ sub {
            my $responder = shift;

            # this idiom here borrows heavily from the documentation on this
            # blog post, by tatsuhiko miyagawa:
            #
            #   http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html
            #
            # this effectively allows the user of this api to stream data to
            # the client.

            # finalize will always return a three element array. the third
            # element is supposed to be the body. because we don't have a body
            # yet (it's in the callback), this uses splice to exclude the third
            # element (aka the body) and just return the status code and the
            # list of headers.
            my $writer = $responder->([splice(@{$self->{'_response'}->finalize()}, 0, 2)]);
            return $self->{'_callback'}->($writer);
        } ];
    }

    # just return a normal response
    return $self->{'_response'}->finalize();
}

sub _bake_cookie {
    my ($self, $key, $value) = @_;

    my @cookie = (URI::Escape::uri_escape($key) . "=" . URI::Escape::uri_escape($value->{'value'}));
    push(@cookie, "domain="  . $value->{'domain'})                       if $value->{'domain'};
    push(@cookie, "path="    . $value->{'path'})                         if $value->{'path'};
    push(@cookie, "expires=" . $self->_cookie_date($value->{'expires'})) if $value->{'expires'};
    push(@cookie, "secure")                                              if $value->{'secure'};
    push(@cookie, "HttpOnly")                                            if $value->{'httponly'};
    return join("; ", @cookie);

}

my @MON  = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );

sub _cookie_date {
    my ($self, $expires) = @_;

    if ($expires =~ /^\-?\d+$/x) {
        # all numbers -> epoch date
        # (cookies use '-' as date separator, HTTP uses ' ')
        my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($expires);
        $year += 1900;

        return sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
                       $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec);
    }

    return $expires;
}

1;

=head1 NAME

Prancer::Response

=head1 SYNOPSIS

    sub handler {
        my ($self, $env, $request, $response, $session) = @_;

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

                $writer->write("What is up?");
                $writer->close();
            });
            return $response->finalize(200);
        }
    }

=head1 METHODS

=over

=item header

This method expects a list of headers to add to the response. For example:

    $response->header("Content-Type" => "text/plain");
    $response->header("Content-Length" => 1234, "X-Foo" => "bar");

If the header has already been set this will add another value to it and the
response will include the same header multiple times. To replace a header that
has already been set, remove the existing value first:

    $response->headers->remove("X-Foo");

=item headers

Returns a L<Hash::MultiValue> of all headers that have been set to be sent with
the response.

=item cookie

If called with no arguments this will return the names of all cookies that have
been set to be sent with the response. Otherwise, this method expects a list of
cookies to add to the response. For example:

    $response->cookie("foo" => {
        'value'   => "test",
        'path'    => "/",
        'domain'  => ".example.com",
        'expires' => time + 24 * 60 * 60,
    });

The hashref may contain the keys C<value>, C<domain>, C<expires>, C<path>,
C<httponly>, and C<secure>. C<expires> can take a string or an integer (as an
epoch time) and B<does not> convert string formats like C<+3M>.

=item cookies

Returns a L<Hash::MultiValue> of all cookies that have been set to be sent with
the response.

=item body

Send buffered output to the client. Anything sent to the client with this
method will be buffered until C<finalize> is called. For example:

    $response->body("hello");
    $response->body("goodbye", "world");

If a buffered response is not desired then the body may be a callback to send a
streaming response to the client. Any headers or response codes set in the
callback will be ignored as they must all be set beforehand. Any body set
before a callback is set will also be ignored. For example:

    $response->body(sub {
        my $writer = shift;
        $writer->write("Hello, world!");
        $writer->close();
        return;
    });

=item finalize

This requires one argument: the HTTP status code of the response. It will then
send a PSGI compatible response to the client. For example:

    # or hard code it
    $response->finalize(200);

=back

=cut



( run in 0.747 second using v1.01-cache-2.11-cpan-39bf76dae61 )