Permalink
Browse files

Merge branch 'release/1.3092'

  • Loading branch information...
2 parents dfe5e6f + fc7e4f6 commit 9de2e17d0752bc90d74ff82c3c4e9b5d1ffd37d8 @xsawyerx xsawyerx committed Jan 27, 2012
View
@@ -1,5 +1,28 @@
{{$NEXT}}
+1.3092 27.01.2012
+
+ [ BUG FIXES ]
+ * Don't call isa() on unblessed refs in Dancer::Exception. (Sam Kington)
+ * Assume UTF-8 by default when serialising JSON. (Sam Kington)
+ * GH #725: If a cookie is set multiple times, last value wins.
+ (David Precious)
+ * More intuitive, backwards compatible appending of default template
+ extension. (GH #716, David Precious)
+ * Prevent recursion in censoring. (Yanick Champoux, Damien dams Krotkine)
+ * GH #734: More tests flexibility (Sawyer X, reported by @birdy-)
+
+ [ ENHANCEMENTS ]
+ * Return the current set prefix using prefix(). (Michal Wojciechowski)
+ * More intuitive appending of default template extension. Makes for cleaner
+ more DWIM code. (David Precious, reported by Nick Knutov)
+ * Allow any options to JSON serializer. (Lee Johnson)
+ * Support complex views with multiple document roots. (Pedro Melo)
+
+ [ DOCUMENTATION ]
+ * Document how to work with Dotcloud. (Oliver Gorwits)
+ * Clean ups and fix ups. (David Precious, Sawyer X, Michal Wojciechowski)
+
1.3091 17.12.2011
[ BUG FIXES ]
View
@@ -5,7 +5,7 @@ use warnings;
use Carp;
use Cwd 'realpath';
-our $VERSION = '1.3091';
+our $VERSION = '1.3092';
our $AUTHORITY = 'SUKRIA';
use Dancer::App;
@@ -179,7 +179,8 @@ sub pass { Dancer::SharedData->response->pass(1);
sub patch { Dancer::App->current->registry->universal_add('patch', @_) }
sub path { Dancer::FileUtils::path(@_) }
sub post { Dancer::App->current->registry->universal_add('post', @_) }
-sub prefix { Dancer::App->current->set_prefix(@_) }
+sub prefix { @_ == 0 ? Dancer::App->current->get_prefix :
+ Dancer::App->current->set_prefix(@_) }
sub put { Dancer::App->current->registry->universal_add('put', @_) }
sub redirect { goto &_redirect }
sub render_with_layout { Dancer::Template::Abstract->_render_with_layout(@_) }
View
@@ -35,6 +35,11 @@ sub set_app_prefix {
$self->prefix($prefix);
}
+sub get_prefix {
+ # return the current prefix (if undefined, return an empty string)
+ return Dancer::App->current->prefix || '';
+}
+
sub set_prefix {
my ($self, $prefix, $cb) = @_;
@@ -128,7 +128,7 @@ requests.
The L<params|Dancer/params> keyword returns a hashref of request parameters;
these will be parameters supplied on the query string, within the path itself
-(with named placeholders), and, for HTTTP POST requests, the content of the
+(with named placeholders), and, for HTTP POST requests, the content of the
POST body.
@@ -63,8 +63,7 @@ sub set_cookie {
sub set_cookie_object {
my ($class, $name, $cookie) = @_;
- Dancer::SharedData->response->push_header(
- 'Set-Cookie' => $cookie->to_header);
+ Dancer::SharedData->response->add_cookie($name, $cookie);
Dancer::Cookies->cookies->{$name} = $cookie;
}
@@ -43,11 +43,12 @@ Note that when using fast-cgi your rewrite rule should be:
RewriteRule ^(.*)$ /dispatch.fcgi$1 [QSA,L]
-Here, the mod_rewrite magic for Pretty-URLs is directly put in Apache's configuration.
-But if your web server supports .htaccess files, you can drop those lines in a .htaccess file.
+Here, the mod_rewrite magic for Pretty-URLs is directly put in Apache's
+configuration. But if your web server supports .htaccess files, you can drop
+those lines in a .htaccess file.
-To check if your server supports mod_rewrite type C<apache2 -l> to list modules.
-To enable mod_rewrite (Debian), run C<a2enmod rewrite>. Place following code in
+To check if your server supports mod_rewrite type C<apache2 -l> to list modules.
+To enable mod_rewrite (Debian), run C<a2enmod rewrite>. Place following code in
a file called .htaccess in your application's root folder:
# BEGIN dancer application htaccess
@@ -65,14 +66,15 @@ embedded web server.
This option is a no-brainer, easy to setup, low maintenance but serves requests
slower than all other options.
-You can use the same technique to deploy with FastCGI, by just changing the line:
-
+You can use the same technique to deploy with FastCGI, by just changing the
+line:
+
AddHandler cgi-script .cgi
By:
AddHandler fastcgi-script .fcgi
-
+
Of course remember to update your rewrite rules, if you have set any:
RewriteRule (.*) /dispatch.fcgi$1 [L]
@@ -102,7 +104,8 @@ A number of Perl web servers supporting PSGI are available on cpan:
=item Starman
-C<Starman> is a high performance web server, with support for preforking, signals, ...
+C<Starman> is a high performance web server, with support for preforking,
+signals and more.
=item Twiggy
@@ -204,13 +207,72 @@ and now use L<Starman>
plackup -a app.psgi -s Starman
+=head3 Hosting on DotCloud
+
+The simplest way to achieve this is to push your main application directory
+to dotcloud with your C<bin/app.pl> file copied to (or symlinked from)
+C<app.psgi>.
+
+Beware that the dotcloud service enforces one environment only, named
+C<deployment>. So instead of having C<environments/development.yml> or
+C<environments/production.yml> you I<must> have a file named
+C<environments/deployment.yml>.
+
+Also make sure that your C<Makefile.PL> (or other dependency mechanism) includes
+both Dancer and L<Plack::Request>.
+
+The default in-memory session handler won't work, and instead you should switch
+to something persistent. Edit C<config.yml> to change C<session: 'Simple'> to
+(for example) C<session: 'YAML'>.
+
+=head4 In case you have issues with Template::Toolkit on Dotcloud
+
+If you use the L<Template::Toolkit> and its C<INCLUDE> or C<PROCESS> directives,
+you might need to add the search path of your view files to the config. This is
+probably going to be something like
+C<INCLUDE_PATH: '/home/dotcloud/current/views'> in C<config.yml>.
+
+An alternative implementation is to use a variation of the above Plack::Builder
+template:
+
+ use Plack::Builder;
+ use Dancer ':syntax';
+ use Dancer::Handler;
+ use lib 'lib';
+
+ my $app1 = sub {
+ setting appdir => '/home/dotcloud/current';
+ load_app "My::App";
+ Dancer::App->set_running_app("My::App");
+ my $env = shift;
+ Dancer::Handler->init_request_headers($env);
+ my $req = Dancer::Request->new(env => $env);
+ Dancer->dance($req);
+ };
+
+ builder {
+ mount "/app1" => $app1;
+ };
+
+This also supports hosting multiple apps, but you probably also need to specify
+the specific Environment configuration to use in your application.
+
+When mounting under a path on dotcloud, as in the above example, always create
+links using the C<uri_for()> method for Dancer routes, and a C<uri_base>
+variable for static content as shown in L<Dancer::Cookbook>. This means
+whatever base path your app is mounted under, links and form submissions will
+continue to work.
+
=head3 Creating a service
-You can turn your app into proper service running in background using one of the following examples:
+You can turn your app into proper service running in background using one of
+the following examples:
=head4 Using Ubic
-L<Ubic> is an extensible perlish service manager. You can use it to start and stop any services, automatically start them on reboots or daemon failures, and implement custom status checks.
+L<Ubic> is an extensible perlish service manager. You can use it to start and
+stop any services, automatically start them on reboots or daemon failures, and
+implement custom status checks.
A basic PSGI service description (usually in /etc/ubic/service/application):
@@ -235,7 +297,8 @@ Run C<ubic start application> to start the service.
=head4 Using daemontools
-daemontools is a collection of tools for managing UNIX services. You can use it to easily start/restart/stop services.
+daemontools is a collection of tools for managing UNIX services. You can use it
+to easily start/restart/stop services.
A basic script to start an application: (in /service/application/run)
@@ -272,13 +335,14 @@ under a specified dir:
ProxyPass /mywebapp/ http://localhost:3000/
ProxyPassReverse /mywebapp/ http://localhost:3000/
-It is important for you to note that the Apache2 modules mod_proxy and mod_proxy_http
-must be enabled.
+It is important for you to note that the Apache2 modules mod_proxy and
+mod_proxy_http must be enabled.
a2enmod proxy
a2enmod proxy_http
-It is also important to set permissions for proxying for security purposes, below is an example.
+It is also important to set permissions for proxying for security purposes,
+below is an example.
<Proxy *>
Order allow,deny
@@ -287,8 +351,8 @@ It is also important to set permissions for proxying for security purposes, belo
=head4 Using perlbal
-C<perlbal> is a single-threaded event-based server written in Perl supporting HTTP load
-balancing, web serving, and a mix of the two, available from
+C<Perlbal> is a single-threaded event-based server written in Perl supporting
+HTTP load balancing, web serving, and a mix of the two, available from
L<http://www.danga.com/perlbal/>
It processes hundreds of millions of requests a day just for LiveJournal, Vox
@@ -356,7 +420,8 @@ You can use Lighttp's mod_proxy:
)
}
-This configuration will proxy all request to the B</application> path to the path B</> on localhost:3000.
+This configuration will proxy all request to the B</application> path to the
+path B</> on localhost:3000.
=head4 Using Nginx
View
@@ -137,7 +137,17 @@ sub dumper {
# Given a hashref, censor anything that looks sensitive. Returns number of
# items which were "censored".
sub _censor {
- my $hash = shift;
+ my ( $hash, $recursecount ) = @_;
+ $recursecount ||= 0;
+
+ # we're checking recursion ourselves, no need to warn
+ no warnings 'recursion';
+
+ if ( $recursecount++ > 100 ) {
+ warn "Data exceeding 100 levels, truncating\n";
+ return $hash;
+ }
+
if (!$hash || ref $hash ne 'HASH') {
carp "_censor given incorrect input: $hash";
return;
@@ -146,7 +156,7 @@ sub _censor {
my $censored = 0;
for my $key (keys %$hash) {
if (ref $hash->{$key} eq 'HASH') {
- $censored += _censor($hash->{$key});
+ $censored += _censor( $hash->{$key}, $recursecount );
}
elsif ($key =~ /(pass|card?num|pan|secret)/i) {
$hash->{$key} = "Hidden (looks potentially sensitive)";
@@ -3,6 +3,7 @@ package Dancer::Exception;
use strict;
use warnings;
use Carp;
+use Scalar::Util qw(blessed);
our $Verbose = 0;
@@ -26,12 +27,12 @@ sub catch (&;@) {
my @new_rest = grep { ref ne 'Try::Tiny::Catch' or $continuation_code = $$_, 0 } @rest;
$continuation_code
and return ( bless( \ sub {
- ref && $_->isa('Dancer::Continuation')
+ ref && blessed($_) && $_->isa('Dancer::Continuation')
? $continuation_code->(@_) : $block->(@_);
}, 'Try::Tiny::Catch') , @new_rest);
return ( bless ( \ sub {
- ref && $_->isa('Dancer::Continuation')
+ ref && blessed($_) && $_->isa('Dancer::Continuation')
? die($_) : $block->(@_) ;
}, 'Try::Tiny::Catch'), @new_rest );
}
@@ -43,7 +44,7 @@ sub continuation (&;@) {
my @new_rest = grep { ref ne 'Try::Tiny::Catch' or $catch_code = $$_, 0 } @rest;
$catch_code
and return ( bless( \ sub {
- ref && $_->isa('Dancer::Continuation')
+ ref && blessed($_) && $_->isa('Dancer::Continuation')
? $block->(@_) : $catch_code->(@_);
}, 'Try::Tiny::Catch') , @new_rest);
@@ -127,7 +127,7 @@ be set in the params hashref.
Tokens can be optional, for example:
get '/hello/:name?' => sub {
- "Hello there " . param('name') || "whoever you are!";
+ "Hello there " . (param('name') || "whoever you are!");
};
@@ -143,6 +143,9 @@ sub headers {
sub headers_to_array {
my $self = shift;
+ # Time to finalise cookie headers, now
+ $self->build_cookie_headers;
+
my $headers = [
map {
my $k = $_;
@@ -157,8 +160,34 @@ sub headers_to_array {
return $headers;
}
+# Given a cookie name and object, add it to the cookies we're going to send.
+# Stores them in a hashref within the response object until the response is
+# being built, so that, if the same cookie is set multiple times, only the last
+# value given to it will appear in a Set-Cookie header.
+sub add_cookie {
+ my ($self, $name, $cookie) = @_;
+ if ($self->{_built_cookies}) {
+ die "Too late to set another cookie, headers already built";
+ }
+ $self->{_cookies}{$name} = $cookie;
+}
+
+
+# When the response is about to be rendered, that's when we build up the
+# Set-Cookie headers
+sub build_cookie_headers {
+ my $self = shift;
+ for my $name (keys %{ $self->{_cookies} }) {
+ my $header = $self->{_cookies}{$name}->to_header;
+ $self->push_header(
+ 'Set-Cookie' => $header,
+ );
+ }
+ $self->{_built_cookies}++;
+}
1;
+
=head1 NAME
Dancer::Response - Response object for Dancer
@@ -74,8 +74,14 @@ sub process_request {
Dancer::Factory::Hook->execute_hooks('before_deserializer');
return $request unless engine;
- return $request
- unless engine->support_content_type($request->content_type);
+
+ # Content-Type may contain additional parameters
+ # (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7)
+ # which should be safe to ignore at this level.
+ # So accept either e.g. text/xml or text/xml; charset=utf-8
+ my $content_type = $request->content_type;
+ $content_type =~ s/ \s* ; .+ $ //x;
+ return $request unless engine->support_content_type($content_type);
return $request
unless $request->is_put || $request->is_post || $request->is_patch;
@@ -84,7 +90,9 @@ sub process_request {
# try to deserialize
my $new_params;
- eval { $new_params = engine->deserialize($request->body) };
+ eval {
+ $new_params = engine->deserialize($request->body)
+ };
if ($@) {
Dancer::Logger::core "Unable to deserialize request body with "
. engine()
Oops, something went wrong.

0 comments on commit 9de2e17

Please sign in to comment.