The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 9103
MANIFEST 02
META.json 24
META.yml 24
Makefile.PL 03
README.md 22
examples/hello-template.pl 012
lib/Mojo/Asset/File.pm 94
lib/Mojo/Base.pm 52
lib/Mojo/ByteStream.pm 11
lib/Mojo/Collection.pm 1148
lib/Mojo/Content.pm 65
lib/Mojo/DOM/CSS.pm 167
lib/Mojo/DOM/HTML.pm 11
lib/Mojo/DOM.pm 77131
lib/Mojo/Date.pm 1657
lib/Mojo/Headers.pm 614
lib/Mojo/IOLoop/Client.pm 1792
lib/Mojo/IOLoop/Delay.pm 22
lib/Mojo/IOLoop.pm 56
lib/Mojo/JSON/Pointer.pm 31
lib/Mojo/JSON.pm 32
lib/Mojo/Loader.pm 11
lib/Mojo/Message/Request.pm 2320
lib/Mojo/Message/Response.pm 21
lib/Mojo/Message.pm 67
lib/Mojo/Parameters.pm 34
lib/Mojo/Path.pm 12
lib/Mojo/Reactor.pm 103
lib/Mojo/Server/CGI.pm 32
lib/Mojo/Server/Daemon.pm 99
lib/Mojo/Server/Hypnotoad.pm 99
lib/Mojo/Server/Morbo.pm 910
lib/Mojo/Server/PSGI.pm 32
lib/Mojo/Server/Prefork.pm 910
lib/Mojo/Server.pm 62
lib/Mojo/Transaction/WebSocket.pm 4130
lib/Mojo/URL.pm 63
lib/Mojo/UserAgent/Proxy.pm 55
lib/Mojo/UserAgent/Transactor.pm 1027
lib/Mojo/UserAgent.pm 7492
lib/Mojo/Util.pm 241
lib/Mojo.pm 172
lib/Mojolicious/Command/generate/app.pm 22
lib/Mojolicious/Command/generate/lite_app.pm 11
lib/Mojolicious/Command/get.pm 11
lib/Mojolicious/Command/routes.pm 52
lib/Mojolicious/Command/test.pm 175
lib/Mojolicious/Command/version.pm 910
lib/Mojolicious/Command.pm 21
lib/Mojolicious/Commands.pm 41
lib/Mojolicious/Controller.pm 1739
lib/Mojolicious/Guides/Cookbook.pod 345
lib/Mojolicious/Guides/FAQ.pod 23
lib/Mojolicious/Guides/Growing.pod 2126
lib/Mojolicious/Guides/Rendering.pod 43104
lib/Mojolicious/Guides/Routing.pod 3651
lib/Mojolicious/Lite.pm 11
lib/Mojolicious/Plugin/DefaultHelpers.pm 424
lib/Mojolicious/Plugin/EPLRenderer.pm 137
lib/Mojolicious/Plugin/EPRenderer.pm 2123
lib/Mojolicious/Plugin/PODRenderer.pm 3823
lib/Mojolicious/Plugin/TagHelpers.pm 22
lib/Mojolicious/Plugins.pm 76
lib/Mojolicious/Renderer.pm 3557
lib/Mojolicious/Routes/Match.pm 99
lib/Mojolicious/Routes/Pattern.pm 4742
lib/Mojolicious/Routes/Route.pm 2013
lib/Mojolicious/Routes.pm 95
lib/Mojolicious/Sessions.pm 01
lib/Mojolicious/Static.pm 2783
lib/Mojolicious/Validator/Validation.pm 11
lib/Mojolicious/templates/development.html.ep 55
lib/Mojolicious/templates/perldoc.html.ep 411
lib/Mojolicious.pm 1722
lib/Test/Mojo.pm 2121
lib/ojo.pm 1123
t/mojo/asset.t 02
t/mojo/collection.t 29
t/mojo/daemon.t 08
t/mojo/date.t 033
t/mojo/dom.t 047
t/mojo/json.t 11
t/mojo/response.t 20
t/mojo/transactor.t 031
t/mojo/user_agent.t 310
t/mojo/user_agent_socks.t 0172
t/mojo/util.t 22
t/mojo/websocket_frames.t 013
t/mojo/websocket_proxy_tls.t 225
t/mojolicious/app.t 433
t/mojolicious/lite_app.t 08
t/mojolicious/ojo.t 014
t/mojolicious/pattern.t 08
t/mojolicious/pod_renderer_lite_app.t 56
t/mojolicious/production_app.t 04
t/mojolicious/renderer.t 052
t/mojolicious/routes.t 032
t/mojolicious/static_lite_app.t 067
t/mojolicious/twinkle_lite_app.t 33
t/mojolicious/websocket_lite_app.t 05
101 files changed (This is a version diff) 9212040
@@ -1,4 +1,99 @@
 
+5.33  2014-08-24
+  - Improved Mojo::Date to be able to handle higher precision times.
+  - Improved Mojo::ByteStream performance.
+
+5.32  2014-08-21
+  - Added to_datetime method to Mojo::Date.
+  - Improved Mojo::Date to support RFC 3339.
+
+5.31  2014-08-19
+  - Improved Mojolicious::Static to allow custom content types.
+  - Improved url_for performance.
+
+5.30  2014-08-17
+  - Improved Mojolicious::Static to only handle GET and HEAD requests.
+  - Improved Mojo::URL performance.
+  - Improved url_for performance slightly.
+  - Fixed bug where DATA sections sometimes got corrupted after forking, which
+    caused applications to fail randomly.
+  - Fixed Mojo::IOLoop::Client to use a timeout for every connection.
+
+5.29  2014-08-16
+  - Added helpers method to Mojolicious::Controller.
+  - Improved performance of .ep templates slightly.
+  - Fixed "0" value bug in Mojolicious::Plugin::EPRenderer.
+
+5.28  2014-08-13
+  - Improved performance of nested helpers and helpers in templates
+    significantly.
+  - Improved Mojo::JSON to generate smaller JSON by not escaping the "/"
+    character.
+
+5.27  2014-08-11
+  - Added support for nested helpers.
+  - Added get_helper method to Mojolicious::Renderer.
+  - Added n function to ojo.
+  - Fixed bug in Mojolicious::Routes::Match where placeholder values got
+    merged too early.
+
+5.26  2014-08-09
+  - Improved WebSocket performance.
+  - Fixed proxy exception handling bug in Mojo::UserAgent.
+  - Fixed bug where Mojo::Transaction::WebSocket would build incorrect frames
+    if the FIN bit was not set.
+
+5.25  2014-08-07
+  - Added reduce method to Mojo::Collection. (sri, batman)
+  - Added if_none_match method to Mojo::Headers.
+  - Added is_fresh method to Mojolicious::Static.
+  - Added is_fresh helper to Mojolicious::Plugin::DefaultHelpers.
+  - Improved Mojolicious to use MyApp::Controller namespace by default and
+    encourage its use in the documentation.
+  - Improved sort method in Mojo::Collection to use $a and $b. (batman)
+  - Improved Mojolicious::Static to support ETag and If-None-Match headers.
+  - Improved documentation browser CSS.
+  - Fixed escaping bugs in Mojo::DOM::CSS.
+
+5.24  2014-08-02
+  - Improved url_escape performance slightly.
+  - Fixed memory leak in Mojo::IOLoop::Client.
+  - Fixed bug where ojo would sometimes die silently.
+
+5.23  2014-07-31
+  - Improved router performance.
+  - Improved routes command to show format regular expression separately.
+  - Fixed partial route bug in Mojolicious::Routes::Match.
+  - Fixed format detection bug in Mojolicious::Routes::Pattern.
+
+5.22  2014-07-30
+  - Added SOCKS5 support to Mojo::UserAgent.
+  - Added socks_address, socks_pass, socks_port and socks_user options to
+    Mojo::IOLoop::Client::connect.
+  - Improved documentation browser CSS.
+
+5.21  2014-07-27
+  - Improved handling of Pod::Simple::XHTML 3.09 dependency.
+  - Improved documentation browser CSS.
+
+5.20  2014-07-27
+  - Fixed a few bugs in Mojolicious::Plugin::PODRenderer by switching from
+    Pod::Simple::HTML to Pod::Simple::XHTML.
+  - Fixed Perl 5.18.x compatibility.
+
+5.19  2014-07-26
+  - Improved support for Unicode anchors in Mojolicious::Plugin::PODRenderer.
+  - Fixed is_readable scalability problems in Mojo::Reactor.
+
+5.18  2014-07-25
+  - Improved is_readable performance in Mojo::Reactor.
+
+5.17  2014-07-24
+  - Welcome to the Mojolicious core team Jan Henning Thorsen.
+  - Added val method to Mojo::DOM. (batman, sri)
+  - Improved Mojo::Collection performance.
+  - Fixed support for Unicode anchors in Mojolicious::Plugin::PODRenderer.
+
 5.16  2014-07-21
   - Improved Mojo::Asset::File to allow appending data to existing files.
     (iakuf, sri)
@@ -91,7 +186,7 @@
     invocant.
   - Changed return value of path_for method in Mojolicious::Routes::Match.
   - Changed return value and arguments of error method in Mojo::Message.
-  - Removed deprecated support for "X-Forwarded-HTTPS".
+  - Removed deprecated support for X-Forwarded-HTTPS.
   - Removed return values from wait method in Mojo::IOLoop::Delay.
   - Removed list context support from header method in Mojo::Headers.
   - Removed generate_port method from Mojo::IOLoop.
@@ -129,8 +224,7 @@
   - Improved accept performance in Mojo::IOLoop::Server.
 
 4.97  2014-04-30
-  - Deprecated support for "X-Forwarded-HTTPS" in favor of
-    "X-Forwarded-Proto".
+  - Deprecated support for X-Forwarded-HTTPS in favor of X-Forwarded-Proto.
   - Added multi-name support to param method in Mojo::Parameters.
 
 4.96  2014-04-28
@@ -2601,7 +2695,7 @@
   - Fixed size limits in message parser.
 
 1.18  2011-04-19
-  - Added support for "X-Forwarded-HTTPS" and "X-Forwarded-Host" headers.
+  - Added support for X-Forwarded-HTTPS and X-Forwarded-Host headers.
   - Added argument localization to the include helper. (crab, moritz, sri)
   - Fixed test case.
 
@@ -3131,7 +3225,7 @@
       print $client->get('http://search.cpan.org')->res->body;
       my $tx = $client->post_form('http://kraih.com', {q => 'mojo'});
   - Made plugins much more configurable.
-  - Improved PSGI support and added "psgi" command.
+  - Improved PSGI support and added psgi command.
   - Added automatic environment detection for Plack based servers, there is
     no technical way to detect all PSGI compliant servers yet though. That
     means "plackup myapp.pl" and "plackup script/myapp" should just work.
@@ -3158,8 +3252,8 @@
   - Added I18N support. (vti, memowe)
   - Added template detection again, you can now just mix multiple template
     engines and the renderer will automatically pick the right template. So
-    you don't have to use the "handler" argument anymore, even though it's
-    still available and overrides auto detection.
+    you don't have to use the handler argument anymore, even though it's still
+    available and overrides auto detection.
   - Added Flash Policy Server example. (xantus)
   - Added more reference docs.
   - Added ".gitignore" generator command. (marcus)
@@ -3440,8 +3534,8 @@
   - Merged eplite and epl, this change is not backwards compatible, you will
     have to rename all your eplite templates to epl.
   - Simplified MojoX::Renderer, this change is not backwards compatible!
-    Handler can no longer be detected, that means "default_handler" or the
-    "handler" argument are required. The template argument can no longer
+    Handler can no longer be detected, that means default_handler or the
+    handler argument are required. The template argument can no longer
     contain format or handler.
       $self->render(template => 'foo.html.epl');
     becomes
@@ -4,6 +4,7 @@ CONTRIBUTING.md
 examples/connect-proxy.pl
 examples/entities.pl
 examples/fast.pl
+examples/hello-template.pl
 examples/hello.pl
 examples/microhttpd.pl
 examples/websocket.pl
@@ -237,6 +238,7 @@ t/mojo/transactor.t
 t/mojo/url.t
 t/mojo/user_agent.t
 t/mojo/user_agent_online.t
+t/mojo/user_agent_socks.t
 t/mojo/user_agent_tls.t
 t/mojo/util.t
 t/mojo/websocket.t
@@ -4,7 +4,7 @@
       "Sebastian Riedel <sri@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520",
+   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.142060",
    "license" : [
       "artistic_2"
    ],
@@ -33,6 +33,8 @@
       },
       "runtime" : {
          "requires" : {
+            "Pod::Simple" : "3.09",
+            "Time::Local" : "1.2",
             "perl" : "5.010001"
          }
       }
@@ -51,5 +53,5 @@
       },
       "x_MailingList" : "http://groups.google.com/group/mojolicious"
    },
-   "version" : "5.16"
+   "version" : "5.33"
 }
@@ -7,7 +7,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520'
+generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.142060'
 license: artistic_2
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -19,6 +19,8 @@ no_index:
     - inc
     - t
 requires:
+  Pod::Simple: '3.09'
+  Time::Local: '1.2'
   perl: '5.010001'
 resources:
   MailingList: http://groups.google.com/group/mojolicious
@@ -26,4 +28,4 @@ resources:
   homepage: http://mojolicio.us
   license: http://www.opensource.org/licenses/artistic-license-2.0
   repository: http://github.com/kraih/mojo
-version: '5.16'
+version: '5.33'
@@ -5,6 +5,8 @@ use warnings;
 
 use ExtUtils::MakeMaker;
 
+# Pod::Simple 3.09 first shipped with Perl 5.11.2
+# Time::Local 1.2 first shipped with Perl 5.13.9
 WriteMakefile(
   NAME         => 'Mojolicious',
   VERSION_FROM => 'lib/Mojolicious.pm',
@@ -22,6 +24,7 @@ WriteMakefile(
     },
     no_index => {directory => ['t']}
   },
+  PREREQ_PM => {'Pod::Simple' => '3.09', 'Time::Local' => '1.2'},
   EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'],
   test => {TESTS => 't/*.t t/*/*.t'}
 );
@@ -23,8 +23,8 @@
     recommended, and optional CPAN modules will be used to provide advanced
     functionality if they are installed).
   * Full stack HTTP and WebSocket client/server implementation with IPv6, TLS,
-    SNI, IDNA, Comet (long polling), keep-alive, connection pooling, timeout,
-    cookie, multipart, proxy, and gzip compression support.
+    SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling), keep-alive, connection
+    pooling, timeout, cookie, multipart, and gzip compression support.
   * Built-in non-blocking I/O web server, supporting multiple event loops as
     well as optional preforking and hot deployment, perfect for embedding.
   * Automatic CGI and [PSGI](http://plackperl.org) detection.
@@ -0,0 +1,12 @@
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Mojolicious::Lite;
+
+get '/hello';
+
+# Minimal "Hello World" application with template for profiling
+app->start;
+__DATA__
+
+@@ hello.html.ep
+Hello World!
@@ -78,8 +78,7 @@ sub contains {
     # Search window
     my $pos = index $window, $str;
     return $offset + $pos if $pos >= 0;
-    $offset += $read;
-    return -1 if $read == 0 || $offset == $end;
+    return -1 if $read == 0 || ($offset += $read) == $end;
 
     # Resize window
     substr $window, 0, $read, '';
@@ -98,8 +97,7 @@ sub get_chunk {
 
   my $buffer;
   if (defined(my $end = $self->end_range)) {
-    my $chunk = $end + 1 - $offset;
-    return '' if $chunk <= 0;
+    return '' if (my $chunk = $end + 1 - $offset) <= 0;
     $handle->sysread($buffer, $chunk > $max ? $max : $chunk);
   }
   else { $handle->sysread($buffer, $max) }
@@ -128,11 +126,8 @@ sub size {
 }
 
 sub slurp {
-  my $handle = shift->handle;
-  $handle->sysseek(0, SEEK_SET);
-  my $content = '';
-  while ($handle->sysread(my $buffer, 131072)) { $content .= $buffer }
-  return $content;
+  return '' unless defined(my $file = shift->path);
+  return Mojo::Util::slurp $file;
 }
 
 1;
@@ -70,11 +70,8 @@ sub attr {
       $code .= ref $default eq 'CODE' ? '$default->($_[0]);' : '$default;';
     }
 
-    # Store value
-    $code .= "\n  }\n  \$_[0]{'$attr'} = \$_[1];\n";
-
-    # Footer (return invocant)
-    $code .= "  \$_[0];\n}";
+    # Footer (store value and return invocant)
+    $code .= "\n  }\n  \$_[0]{'$attr'} = \$_[1];\n  \$_[0];\n}";
 
     warn "-- Attribute $attr in $class\n$code\n\n" if $ENV{MOJO_BASE_DEBUG};
     Carp::croak "Mojo::Base error: $@" unless eval "$code;1";
@@ -1,6 +1,6 @@
 package Mojo::ByteStream;
 use Mojo::Base -strict;
-use overload '""' => sub { shift->to_string }, fallback => 1;
+use overload '""' => sub { ${$_[0]} }, fallback => 1;
 
 use Exporter 'import';
 use Mojo::Collection;
@@ -15,7 +15,7 @@ our @EXPORT_OK = ('c');
 
 sub AUTOLOAD {
   my $self = shift;
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
   return $self->pluck($method, @_);
@@ -26,7 +26,7 @@ sub DESTROY { }
 sub c { __PACKAGE__->new(@_) }
 
 sub compact {
-  shift->grep(sub { length($_ // '') });
+  $_[0]->new(grep { defined $_ && (ref $_ || length $_) } @{$_[0]});
 }
 
 sub each {
@@ -70,7 +70,13 @@ sub new {
 
 sub pluck {
   my ($self, $method, @args) = @_;
-  return $self->map(sub { $_->$method(@args) });
+  return $self->new(map { $_->$method(@args) } @$self);
+}
+
+sub reduce {
+  my $self = shift;
+  @_ = (@_, @$self);
+  goto &List::Util::reduce;
 }
 
 sub reverse { $_[0]->new(reverse @{$_[0]}) }
@@ -86,14 +92,23 @@ sub slice {
 
 sub sort {
   my ($self, $cb) = @_;
-  return $self->new($cb ? sort { $a->$cb($b) } @$self : sort @$self);
+
+  return $self->new(sort @$self) unless $cb;
+
+  my $caller = caller;
+  no strict 'refs';
+  my @sorted = sort {
+    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
+    $a->$cb($b);
+  } @$self;
+  return $self->new(@sorted);
 }
 
 sub tap { shift->Mojo::Base::tap(@_) }
 
 sub uniq {
   my %seen;
-  return shift->grep(sub { !$seen{$_}++ });
+  return $_[0]->new(grep { !$seen{$_}++ } @{$_[0]});
 }
 
 sub _flatten {
@@ -172,6 +187,7 @@ Evaluate callback for each element in collection or return all elements as a
 list if none has been provided. The element will be the first argument passed
 to the callback and is also available as C<$_>.
 
+  # Make a numbered list
   $collection->each(sub {
     my ($e, $count) = @_;
     say "$count: $e";
@@ -188,7 +204,8 @@ return the first one that matched the regular expression, or for which the
 callback returned true. The element will be the first argument passed to the
 callback and is also available as C<$_>.
 
-  my $five = $collection->first(sub { $_ == 5 });
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
 
 =head2 flatten
 
@@ -207,6 +224,7 @@ create a new collection with all elements that matched the regular expression,
 or for which the callback returned true. The element will be the first
 argument passed to the callback and is also available as C<$_>.
 
+  # Find all values that contain the word "mojo"
   my $interesting = $collection->grep(qr/mojo/i);
 
 =head2 join
@@ -216,7 +234,8 @@ argument passed to the callback and is also available as C<$_>.
 
 Turn collection into L<Mojo::ByteStream>.
 
-  $collection->join("\n")->say;
+  # Join all values with commas
+  $collection->join(', ')->say;
 
 =head2 last
 
@@ -232,7 +251,8 @@ Evaluate callback for each element in collection and create a new collection
 from the results. The element will be the first argument passed to the
 callback and is also available as C<$_>.
 
-  my $doubled = $collection->map(sub { $_ * 2 });
+  # Append the word "mojo" to all values
+  my $mojoified = $collection->map(sub { $_ . 'mojo' });
 
 =head2 new
 
@@ -251,6 +271,20 @@ results.
   # Equal to but more convenient than
   my $new = $collection->map(sub { $_->$method(@args) });
 
+=head2 reduce
+
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
+
+Reduce elements in collection with callback, the first element will be used as
+initial value if none has been provided.
+
+  # Calculate the sum of all values
+  my $sum = $collection->reduce(sub { $a + $b });
+
+  # Count how often each value occurs in collection
+  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
+
 =head2 reverse
 
   my $new = $collection->reverse;
@@ -283,7 +317,8 @@ Number of elements in collection.
 Sort elements based on return value of callback and create a new collection
 from the results.
 
-  my $insensitive = $collection->sort(sub { uc(shift) cmp uc(shift) });
+  # Sort values case insensitive
+  my $insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
 
 =head2 tap
 
@@ -303,8 +338,10 @@ In addition to the L</"METHODS"> above, you can also call methods provided by
 all elements in the collection directly and create a new collection from the
 results, similar to L</"pluck">.
 
-  push @$collection, Mojo::DOM->new("<div><h1>$_</h1></div>") for 1 .. 9;
-  say $collection->find('h1')->type('h2')->prepend_content('Test ')->root;
+  # "<h2>Test1</h2><h2>Test2</h2>"
+  my $collection = Mojo::Collection->new(
+    Mojo::DOM->new("<h1>1</h1>"), Mojo::DOM->new("<h1>2</h1>"));
+  $collection->at('h1')->type('h2')->prepend_content('Test')->join;
 
 =head1 OPERATORS
 
@@ -10,6 +10,9 @@ has headers           => sub { Mojo::Headers->new };
 has max_buffer_size   => sub { $ENV{MOJO_MAX_BUFFER_SIZE} || 262144 };
 has max_leftover_size => sub { $ENV{MOJO_MAX_LEFTOVER_SIZE} || 262144 };
 
+my $BOUNDARY_RE
+  = qr!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i;
+
 sub body_contains {
   croak 'Method "body_contains" not implemented by subclass';
 }
@@ -17,10 +20,7 @@ sub body_contains {
 sub body_size { croak 'Method "body_size" not implemented by subclass' }
 
 sub boundary {
-  return undef unless my $type = shift->headers->content_type;
-  $type =~ m!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i
-    and return $1 // $2;
-  return undef;
+  (shift->headers->content_type // '') =~ $BOUNDARY_RE ? $1 // $2 : undef;
 }
 
 sub build_body    { shift->_build('get_body_chunk') }
@@ -179,8 +179,7 @@ sub write_chunk {
 sub _build {
   my ($self, $method) = @_;
 
-  my $buffer = '';
-  my $offset = 0;
+  my ($buffer, $offset) = ('', 0);
   while (1) {
 
     # No chunk yet, try again
@@ -14,13 +14,6 @@ my $ATTR_RE   = qr/
   )?
   \]
 /x;
-my $CLASS_ID_RE = qr/
-  (?:
-    (?:\.((?:\\\.|[^\#.])+))   # Class
-  |
-    (?:\#((?:\\\#|[^.\#])+))   # ID
-  )
-/x;
 my $PSEUDO_CLASS_RE = qr/(?::([\w\-]+)(?:\(((?:\([^)]+\)|[^)])+)\))?)/;
 my $TOKEN_RE        = qr/
   (\s*,\s*)?                         # Separator
@@ -35,8 +28,8 @@ sub match {
   return $tree->[0] ne 'tag' ? undef : _match(_compile(shift), $tree, $tree);
 }
 
-sub select     { shift->_select(0, @_) }
-sub select_one { shift->_select(1, @_) }
+sub select     { _select(0, shift->tree, _compile(@_)) }
+sub select_one { _select(1, shift->tree, _compile(@_)) }
 
 sub _ancestor {
   my ($selectors, $current, $tree) = @_;
@@ -112,9 +105,9 @@ sub _compile {
     push @$selector, ['tag', $tag];
 
     # Class or ID
-    while ($element =~ /$CLASS_ID_RE/go) {
-      push @$selector, ['attr', 'class', _regex('~', $1)] if defined $1;
-      push @$selector, ['attr', 'id',    _regex('',  $2)] if defined $2;
+    while ($element =~ /(?:([.#])((?:\\[.\#]|[^\#.])+))/g) {
+      my ($name, $op) = $1 eq '.' ? ('class', '~') : ('id', '');
+      push @$selector, ['attr', $name, _regex($op, $2)];
     }
 
     # Pseudo classes (":not" contains more selectors)
@@ -232,12 +225,10 @@ sub _regex {
 }
 
 sub _select {
-  my ($self, $one, $selector) = @_;
+  my ($one, $tree, $pattern) = @_;
 
   my @results;
-  my $pattern = _compile($selector);
-  my $tree    = $self->tree;
-  my @queue   = ($tree);
+  my @queue = ($tree);
   while (my $current = shift @queue) {
     my $type = $current->[0];
 
@@ -312,7 +312,7 @@ Mojo::DOM::HTML - HTML/XML engine
 
   # Turn HTML into DOM tree
   my $html = Mojo::DOM::HTML->new;
-  $html->parse('<div><p id="a">A</p><p id="b">B</p></div>');
+  $html->parse('<div><p id="a">Test</p><p id="b">123</p></div>');
   my $tree = $html->tree;
 
 =head1 DESCRIPTION
@@ -20,7 +20,7 @@ use Scalar::Util qw(blessed weaken);
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
@@ -60,7 +60,8 @@ sub attr {
   return $attrs->{$_[0]} // '' unless @_ > 1 || ref $_[0];
 
   # Set
-  %$attrs = (%$attrs, %{ref $_[0] ? $_[0] : {@_}});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$attrs{keys %$values} = values %$values;
 
   return $self;
 }
@@ -176,6 +177,24 @@ sub type {
   return $self;
 }
 
+sub val {
+  my $self = shift;
+
+  # "option"
+  my $type = $self->type;
+  return Mojo::Collection->new($self->{value} // $self->text)
+    if $type eq 'option';
+
+  # "select"
+  return $self->find('option[selected]')->val->flatten if $type eq 'select';
+
+  # "textarea"
+  return Mojo::Collection->new($self->text) if $type eq 'textarea';
+
+  # "input" or "button"
+  return Mojo::Collection->new($self->{value} // ());
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -391,7 +410,7 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
   use Mojo::DOM;
 
   # Parse
-  my $dom = Mojo::DOM->new('<div><p id="a">A</p><p id="b">B</p></div>');
+  my $dom = Mojo::DOM->new('<div><p id="a">Test</p><p id="b">123</p></div>');
 
   # Find
   say $dom->at('#b')->text;
@@ -411,7 +430,7 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
   }
 
   # Modify
-  $dom->div->p->last->append('<p id="c">C</p>');
+  $dom->div->p->last->append('<p id="c">456</p>');
   $dom->find(':not(p)')->strip;
 
   # Render
@@ -459,7 +478,7 @@ Return a L<Mojo::Collection> object containing all nodes in DOM structure as
 L<Mojo::DOM> objects.
 
   # "<p><b>123</b></p>"
-  $dom->parse('<p><!-- test --><b>123<!-- 456 --></b></p>')
+  $dom->parse('<p><!-- Test --><b>123<!-- 456 --></b></p>')
     ->all_contents->grep(sub { $_->node eq 'comment' })->remove->first;
 
 =head2 all_text
@@ -494,11 +513,12 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 Append HTML/XML fragment to this node.
 
-  # "<div><h1>A</h1><h2>B</h2></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->append('<h2>B</h2>')->root;
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')
+    ->append('<h2>123</h2>')->root;
 
-  # "<p>test 123</p>"
-  $dom->parse('<p>test</p>')->at('p')->contents->first->append(' 123')->root;
+  # "<p>Test 123</p>"
+  $dom->parse('<p>Test</p>')->at('p')->contents->first->append(' 123')->root;
 
 =head2 append_content
 
@@ -507,14 +527,16 @@ Append HTML/XML fragment to this node.
 Append HTML/XML fragment or raw content (depending on node type) to this
 node's content.
 
-  # "<div><h1>AB</h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->append_content('B')->root;
+  # "<div><h1>Test123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')
+    ->append_content('123')->root;
 
-  # "<!-- A B --><br>"
-  $dom->parse('<!-- A --><br>')->contents->first->append_content('B ')->root;
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- Test --><br>')
+    ->contents->first->append_content('123 ')->root;
 
-  # "<p>A<i>B</i></p>"
-  $dom->parse('<p>A</p>')->at('p')->append_content('<i>B</i>')->root;
+  # "<p>Test<i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->append_content('<i>123</i>')->root;
 
 =head2 at
 
@@ -529,10 +551,10 @@ from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 =head2 attr
 
-  my $attrs = $dom->attr;
-  my $foo   = $dom->attr('foo');
-  $dom      = $dom->attr({foo => 'bar'});
-  $dom      = $dom->attr(foo => 'bar');
+  my $hash = $dom->attr;
+  my $foo  = $dom->attr('foo');
+  $dom     = $dom->attr({foo => 'bar'});
+  $dom     = $dom->attr(foo => 'bar');
 
 This element's attributes.
 
@@ -559,23 +581,23 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 Return this node's content or replace it with HTML/XML fragment or raw content
 (depending on node type).
 
-  # "<b>test</b>"
-  $dom->parse('<div><b>test</b></div>')->div->content;
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->div->content;
 
-  # "<div><h1>B</h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->content('B')->root;
+  # "<div><h1>123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('123')->root;
 
   # "<div><h1></h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->content('')->root;
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('')->root;
 
-  # " A "
-  $dom->parse('<!-- A --><br>')->contents->first->content;
+  # " Test "
+  $dom->parse('<!-- Test --><br>')->contents->first->content;
 
-  # "<!-- B --><br>"
-  $dom->parse('<!-- A --><br>')->contents->first->content(' B ')->root;
+  # "<!-- 123 --><br>"
+  $dom->parse('<!-- Test --><br>')->contents->first->content(' 123 ')->root;
 
-  # "<p><i>B</i></p>"
-  $dom->parse('<p>A</p>')->at('p')->content('<i>B</i>')->root;
+  # "<p><i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->content('<i>123</i>')->root;
 
 =head2 contents
 
@@ -585,10 +607,10 @@ Return a L<Mojo::Collection> object containing the child nodes of this element
 as L<Mojo::DOM> objects.
 
   # "<p><b>123</b></p>"
-  $dom->parse('<p>test<b>123</b></p>')->at('p')->contents->first->remove;
+  $dom->parse('<p>Test<b>123</b></p>')->at('p')->contents->first->remove;
 
-  # "<!-- test -->"
-  $dom->parse('<!-- test --><b>123</b>')->contents->first;
+  # "<!-- Test -->"
+  $dom->parse('<!-- Test --><b>123</b>')->contents->first;
 
 =head2 find
 
@@ -603,7 +625,12 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
   # Extract information from multiple elements
   my @headers = $dom->find('h1, h2, h3')->text->each;
-  my @links   = $dom->find('a[href]')->attr('href')->each;
+
+  # Count all the different tags
+  my $hash = $dom->find('*')->type->reduce(sub { $a->{$b}++; $a }, {});
+
+  # Find elements with a class that contains dots
+  my @divs = $dom->find('div.foo\.bar')->each;
 
 =head2 match
 
@@ -640,8 +667,8 @@ fragment if necessary.
 Return L<Mojo::DOM> object for next sibling element or C<undef> if there are
 no more siblings.
 
-  # "<h2>B</h2>"
-  $dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h1')->next;
+  # "<h2>123</h2>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h1')->next;
 
 =head2 next_sibling
 
@@ -651,7 +678,7 @@ Return L<Mojo::DOM> object for next sibling node or C<undef> if there are no
 more siblings.
 
   # "456"
-  $dom->parse('<p><b>123</b><!-- test -->456</p>')->at('b')
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')->at('b')
     ->next_sibling->next_sibling;
 
 =head2 node
@@ -683,11 +710,12 @@ Parse HTML/XML fragment with L<Mojo::DOM::HTML>.
 
 Prepend HTML/XML fragment to this node.
 
-  # "<div><h1>A</h1><h2>B</h2></div>"
-  $dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend('<h1>A</h1>')->root;
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')->at('h2')
+    ->prepend('<h1>Test</h1>')->root;
 
-  # "<p>test 123</p>"
-  $dom->parse('<p>123</p>')->at('p')->contents->first->prepend('test ')->root;
+  # "<p>Test 123</p>"
+  $dom->parse('<p>123</p>')->at('p')->contents->first->prepend('Test ')->root;
 
 =head2 prepend_content
 
@@ -696,14 +724,16 @@ Prepend HTML/XML fragment to this node.
 Prepend HTML/XML fragment or raw content (depending on node type) to this
 node's content.
 
-  # "<div><h2>AB</h2></div>"
-  $dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend_content('A')->root;
+  # "<div><h2>Test123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')->at('h2')
+    ->prepend_content('Test')->root;
 
-  # "<!-- A B --><br>"
-  $dom->parse('<!-- B --><br>')->contents->first->prepend_content(' A')->root;
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- 123 --><br>')
+    ->contents->first->prepend_content(' Test')->root;
 
-  # "<p><i>B</i>A</p>"
-  $dom->parse('<p>A</p>')->at('p')->prepend_content('<i>B</i>')->root;
+  # "<p><i>123</i>Test</p>"
+  $dom->parse('<p>Test</p>')->at('p')->prepend_content('<i>123</i>')->root;
 
 =head2 previous
 
@@ -712,8 +742,8 @@ node's content.
 Return L<Mojo::DOM> object for previous sibling element or C<undef> if there
 are no more siblings.
 
-  # "<h1>A</h1>"
-  $dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h2')->previous;
+  # "<h1>Test</h1>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h2')->previous;
 
 =head2 previous_sibling
 
@@ -723,7 +753,7 @@ Return L<Mojo::DOM> object for previous sibling node or C<undef> if there are
 no more siblings.
 
   # "123"
-  $dom->parse('<p>123<!-- test --><b>456</b></p>')->at('b')
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')->at('b')
     ->previous_sibling->previous_sibling;
 
 =head2 remove
@@ -733,7 +763,7 @@ no more siblings.
 Remove this node and return L</"parent">.
 
   # "<div></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->remove;
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->remove;
 
   # "<p><b>456</b></p>"
   $dom->parse('<p>123<b>456</b></p>')->at('p')->contents->first->remove->root;
@@ -744,11 +774,12 @@ Remove this node and return L</"parent">.
 
 Replace this node with HTML/XML fragment and return L</"parent">.
 
-  # "<div><h2>B</h2></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('<h2>B</h2>');
+  # "<div><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->replace('<h2>123</h2>');
 
-  # "<p><b>B</b></p>"
-  $dom->parse('<p>A</p>')->at('p')->contents->[0]->replace('<b>B</b>')->root;
+  # "<p><b>123</b></p>"
+  $dom->parse('<p>Test</p>')->at('p')
+    ->contents->[0]->replace('<b>123</b>')->root;
 
 =head2 root
 
@@ -774,8 +805,8 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 Remove this element while preserving its content and return L</"parent">.
 
-  # "<div>A</div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->strip;
+  # "<div>Test</div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->strip;
 
 =head2 tap
 
@@ -803,8 +834,8 @@ smart whitespace trimming is enabled by default.
 
 Render this node and its content to HTML/XML.
 
-  # "<b>test</b>"
-  $dom->parse('<div><b>test</b></div>')->div->b->to_string;
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->div->b->to_string;
 
 =head2 tree
 
@@ -824,6 +855,24 @@ This element's type.
   # List types of child elements
   say $dom->children->type;
 
+=head2 val
+
+  my $collection = $dom->val;
+
+Extract values from C<button>, C<input>, C<option>, C<select> or C<textarea>
+element and return a L<Mojo::Collection> object containing these values. In
+the case of C<select>, find all C<option> elements it contains that have a
+C<selected> attribute and extract their values.
+
+  # "b"
+  $dom->parse('<input name="a" value="b">')->at('input')->val;
+
+  # "c"
+  $dom->parse('<option value="c">Test</option>')->at('option')->val;
+
+  # "d"
+  $dom->parse('<option>d</option>')->at('option')->val;
+
 =head2 wrap
 
   $dom = $dom->wrap('<div></div>');
@@ -831,17 +880,17 @@ This element's type.
 Wrap HTML/XML fragment around this node, placing it as the last child of the
 first innermost element.
 
-  # "<p>B<b>A</b></p>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<p>B</p>')->root;
+  # "<p>123<b>Test</b></p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p>123</p>')->root;
 
-  # "<div><p><b>A</b></p>B</div>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<div><p></p>B</div>')->root;
+  # "<div><p><b>Test</b></p>123</div>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<div><p></p>123</div>')->root;
 
-  # "<p><b>A</b></p><p>B</p>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<p></p><p>B</p>')->root;
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p></p><p>123</p>')->root;
 
-  # "<p><b>A</b></p>"
-  $dom->parse('<p>A</p>')->at('p')->contents->first->wrap('<b>')->root;
+  # "<p><b>Test</b></p>"
+  $dom->parse('<p>Test</p>')->at('p')->contents->first->wrap('<b>')->root;
 
 =head2 wrap_content
 
@@ -850,11 +899,11 @@ first innermost element.
 Wrap HTML/XML fragment around this node's content, placing it as the last
 children of the first innermost element.
 
-  # "<p><b>BA</b></p>"
-  $dom->parse('<p>A<p>')->at('p')->wrap_content('<b>B</b>')->root;
+  # "<p><b>123Test</b></p>"
+  $dom->parse('<p>Test<p>')->at('p')->wrap_content('<b>123</b>')->root;
 
-  # "<p><b>A</b></p><p>B</p>"
-  $dom->parse('<b>A</b>')->wrap_content('<p></p><p>B</p>');
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->wrap_content('<p></p><p>123</p>');
 
 =head2 xml
 
@@ -871,9 +920,14 @@ automatically available as object methods, which return a L<Mojo::DOM> or
 L<Mojo::Collection> object, depending on number of children. For more power
 and consistent results you can also use L</"children">.
 
-  say $dom->p->text;
-  say $dom->div->[23]->text;
-  say $dom->div->text;
+  # "Test"
+  $dom->parse('<p>Test</p>')->p->text;
+
+  # "123"
+  $dom->parse('<div>Test</div><div>123</div>')->div->[2]->text;
+
+  # "Test"
+  $dom->parse('<div>Test</div>')->div->text;
 
 =head1 OPERATORS
 
@@ -885,8 +939,8 @@ L<Mojo::DOM> overloads the following operators.
 
 Alias for L</"contents">.
 
-  # "<!-- test -->"
-  $dom->parse('<!-- test --><b>123</b>')->[0];
+  # "<!-- Test -->"
+  $dom->parse('<!-- Test --><b>123</b>')->[0];
 
 =head2 bool
 
@@ -901,7 +955,7 @@ Always true.
 Alias for L</"attr">.
 
   # "test"
-  $dom->parse('<div id="test">A</div>')->at('div')->{id};
+  $dom->parse('<div id="test">Test</div>')->at('div')->{id};
 
 =head2 stringify
 
@@ -2,9 +2,14 @@ package Mojo::Date;
 use Mojo::Base -base;
 use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
 
-use Time::Local 'timegm';
+use Time::Local 1.2 'timegm';
 
-has 'epoch';
+has epoch => sub {time};
+
+my $RFC3339_RE = qr/
+  ^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+(?:\.\d+)?)   # Date and time
+  (?:Z|([+-])(\d+):(\d+))?$                        # Offset
+/xi;
 
 my @DAYS   = qw(Sun Mon Tue Wed Thu Fri Sat);
 my @MONTHS = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
@@ -17,14 +22,21 @@ sub parse {
   my ($self, $date) = @_;
 
   # epoch (784111777)
-  return $self->epoch($date) if $date =~ /^\d+$/;
+  return $self->epoch($date) if $date =~ /^\d+$|^\d+\.\d+$/;
 
   # RFC 822/1123 (Sun, 06 Nov 1994 08:49:37 GMT)
+  my $offset = 0;
   my ($day, $month, $year, $h, $m, $s);
   if ($date =~ /^\w+\,\s+(\d+)\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+GMT$/) {
     ($day, $month, $year, $h, $m, $s) = ($1, $MONTHS{$2}, $3, $4, $5, $6);
   }
 
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  elsif ($date =~ $RFC3339_RE) {
+    ($year, $month, $day, $h, $m, $s) = ($1, $2 - 1, $3, $4, $5, $6);
+    $offset = (($8 * 3600) + ($9 * 60)) * ($7 eq '+' ? -1 : 1) if $7;
+  }
+
   # RFC 850/1036 (Sunday, 06-Nov-94 08:49:37 GMT)
   elsif ($date =~ /^\w+\,\s+(\d+)-(\w+)-(\d+)\s+(\d+):(\d+):(\d+)\s+GMT$/) {
     ($day, $month, $year, $h, $m, $s) = ($1, $MONTHS{$2}, $3, $4, $5, $6);
@@ -36,18 +48,27 @@ sub parse {
   }
 
   # Invalid
-  else { return $self }
+  else { return $self->epoch(undef) }
 
   # Prevent crash
-  my $epoch = eval { timegm($s, $m, $h, $day, $month, $year) };
-  return defined $epoch && $epoch >= 0 ? $self->epoch($epoch) : $self;
+  my $epoch = eval { timegm $s, $m, $h, $day, $month, $year };
+  return $self->epoch(
+    (defined $epoch && ($epoch += $offset) >= 0) ? $epoch : undef);
+}
+
+sub to_datetime {
+
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  my ($s, $m, $h, $day, $month, $year) = gmtime(my $epoch = shift->epoch);
+  my $str = sprintf '%04d-%02d-%02dT%02d:%02d:%02d', $year + 1900, $month + 1,
+    $day, $h, $m, $s;
+  return $str . ($epoch =~ /(\.\d+)$/ ? "$1Z" : 'Z');
 }
 
 sub to_string {
-  my $self = shift;
 
   # RFC 7231 (Sun, 06 Nov 1994 08:49:37 GMT)
-  my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime($self->epoch // time);
+  my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime shift->epoch;
   return sprintf '%s, %02d %s %04d %02d:%02d:%02d GMT', $DAYS[$wday], $mday,
     $MONTHS[$month], $year + 1900, $h, $m, $s;
 }
@@ -69,18 +90,15 @@ Mojo::Date - HTTP date
   say $date->epoch;
 
   # Build
-  my $date = Mojo::Date->new(time);
+  my $date = Mojo::Date->new(time + 60);
   say "$date";
 
 =head1 DESCRIPTION
 
 L<Mojo::Date> implements HTTP date and time functions based on
-L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
-L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
-
-  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
-  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
-  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231> and
+L<RFC 3339|http://tools.ietf.org/html/rfc3339>.
 
 =head1 ATTRIBUTES
 
@@ -91,7 +109,7 @@ L<Mojo::Date> implements the following attributes.
   my $epoch = $date->epoch;
   $date     = $date->epoch(784111777);
 
-Epoch seconds.
+Epoch seconds, defaults to the current time.
 
 =head1 METHODS
 
@@ -113,6 +131,7 @@ Parse date.
 
   # Epoch
   say Mojo::Date->new('784111777')->epoch;
+  say Mojo::Date->new('784111777.21')->epoch;
 
   # RFC 822/1123
   say Mojo::Date->new('Sun, 06 Nov 1994 08:49:37 GMT')->epoch;
@@ -123,12 +142,34 @@ Parse date.
   # Ansi C asctime()
   say Mojo::Date->new('Sun Nov  6 08:49:37 1994')->epoch;
 
+  # RFC 3339
+  say Mojo::Date->new('1994-11-06T08:49:37Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37.21Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37+01:00')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37-01:00')->epoch;
+
+=head2 to_datetime
+
+  my $str = $date->to_datetime;
+
+Render L<RFC 3339|http://tools.ietf.org/html/rfc3339> date and time.
+
+  # "1994-11-06T08:49:37Z"
+  Mojo::Date->new(784111777)->to_datetime;
+
+  # "1994-11-06T08:49:37.21Z"
+  Mojo::Date->new(784111777.21)->to_datetime;
+
 =head2 to_string
 
   my $str = $date->to_string;
 
 Render date suitable for HTTP messages.
 
+  # "Sun, 06 Nov 1994 08:49:37 GMT"
+  Mojo::Date->new(784111777)->to_string;
+
 =head1 OPERATORS
 
 L<Mojo::Date> overloads the following operators.
@@ -10,11 +10,12 @@ my %NORMALCASE = map { lc($_) => $_ } (
   qw(Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges),
   qw(Allow Authorization Cache-Control Connection Content-Disposition),
   qw(Content-Encoding Content-Length Content-Range Content-Type Cookie DNT),
-  qw(Date ETag Expect Expires Host If-Modified-Since Last-Modified Link),
-  qw(Location Origin Proxy-Authenticate Proxy-Authorization Range),
-  qw(Sec-WebSocket-Accept Sec-WebSocket-Extensions Sec-WebSocket-Key),
-  qw(Sec-WebSocket-Protocol Sec-WebSocket-Version Server Set-Cookie Status),
-  qw(TE Trailer Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate)
+  qw(Date ETag Expect Expires Host If-Modified-Since If-None-Match),
+  qw(Last-Modified Link Location Origin Proxy-Authenticate),
+  qw(Proxy-Authorization Range Sec-WebSocket-Accept Sec-WebSocket-Extensions),
+  qw(Sec-WebSocket-Key Sec-WebSocket-Protocol Sec-WebSocket-Version Server),
+  qw(Set-Cookie Status TE Trailer Transfer-Encoding Upgrade User-Agent Vary),
+  qw(WWW-Authenticate)
 );
 for my $header (values %NORMALCASE) {
   my $name = lc $header;
@@ -341,7 +342,7 @@ but is very commonly used.
 =head2 etag
 
   my $etag = $headers->etag;
-  $headers = $headers->etag('abc321');
+  $headers = $headers->etag('"abc321"');
 
 Shortcut for the C<ETag> header.
 
@@ -389,6 +390,13 @@ Shortcut for the C<Host> header.
 
 Shortcut for the C<If-Modified-Since> header.
 
+=head2 if_none_match
+
+  my $etag = $headers->if_none_match;
+  $headers = $headers->if_none_match('"abc321"');
+
+Shortcut for the C<If-None-Match> header.
+
 =head2 is_finished
 
   my $bool = $headers->is_finished;
@@ -19,6 +19,13 @@ use constant TLS => $ENV{MOJO_NO_TLS}
 use constant TLS_READ  => TLS ? IO::Socket::SSL::SSL_WANT_READ()  : 0;
 use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
 
+# SOCKS support requires IO::Socket::Socks
+use constant SOCKS => $ENV{MOJO_NO_SOCKS}
+  ? 0
+  : eval 'use IO::Socket::Socks 0.64 (); 1';
+use constant SOCKS_READ  => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ()  : 0;
+use constant SOCKS_WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
+
 has reactor => sub { Mojo::IOLoop->singleton->reactor };
 
 sub DESTROY { shift->_cleanup }
@@ -42,28 +49,59 @@ sub _connect {
 
   my $handle;
   my $reactor = $self->reactor;
-  my $address = $args->{address} ||= 'localhost';
+  my $address = $args->{socks_address} || ($args->{address} ||= 'localhost');
+  my $port = $args->{socks_port} || $args->{port} || ($args->{tls} ? 443 : 80);
   unless ($handle = $self->{handle} = $args->{handle}) {
     my %options = (
       Blocking => 0,
       PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address,
-      PeerPort => $args->{port} || ($args->{tls} ? 443 : 80)
+      PeerPort => $port
     );
     $options{LocalAddr} = $args->{local_address} if $args->{local_address};
     $options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
     my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
     return $self->emit(error => "Couldn't connect: $@")
       unless $self->{handle} = $handle = $class->new(%options);
-
-    # Timeout
-    $self->{timer} = $reactor->timer($args->{timeout} || 10,
-      sub { $self->emit(error => 'Connect timeout') });
   }
   $handle->blocking(0);
 
+  # Timeout
+  $self->{timer} = $reactor->timer($args->{timeout} || 10,
+    sub { $self->emit(error => 'Connect timeout') });
+
   # Wait for handle to become writable
   weaken $self;
-  $reactor->io($handle => sub { $self->_try($args) })->watch($handle, 0, 1);
+  $reactor->io($handle => sub { $self->_ready($args) })->watch($handle, 0, 1);
+}
+
+sub _ready {
+  my ($self, $args) = @_;
+
+  # Retry or handle exceptions
+  my $handle = $self->{handle};
+  return $! == EINPROGRESS ? undef : $self->emit(error => $!)
+    if $handle->isa('IO::Socket::IP') && !$handle->connect;
+  return $self->emit(error => $! = $handle->sockopt(SO_ERROR))
+    unless $handle->connected;
+
+  # Disable Nagle's algorithm
+  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+
+  $self->_try_socks($args);
+}
+
+sub _socks {
+  my ($self, $args) = @_;
+
+  # Connected
+  my $handle = $self->{handle};
+  return $self->_try_tls($args) if $handle->ready;
+
+  # Switch between reading and writing
+  my $err = $IO::Socket::Socks::SOCKS_ERROR;
+  if    ($err == SOCKS_READ)  { $self->reactor->watch($handle, 1, 0) }
+  elsif ($err == SOCKS_WRITE) { $self->reactor->watch($handle, 1, 1) }
+  else                        { $self->emit(error => $err) }
 }
 
 sub _tls {
@@ -80,19 +118,32 @@ sub _tls {
   elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) }
 }
 
-sub _try {
+sub _try_socks {
   my ($self, $args) = @_;
 
-  # Retry or handle exceptions
   my $handle = $self->{handle};
-  return $! == EINPROGRESS ? undef : $self->emit(error => $!)
-    if $handle->isa('IO::Socket::IP') && !$handle->connect;
-  return $self->emit(error => $! = $handle->sockopt(SO_ERROR))
-    unless $handle->connected;
+  return $self->_try_tls($args) unless $args->{socks_address};
+  return $self->emit(
+    error => 'IO::Socket::Socks 0.64 required for SOCKS support')
+    unless SOCKS;
+
+  my %options
+    = (ConnectAddr => $args->{address}, ConnectPort => $args->{port});
+  @options{qw(AuthType Username Password)}
+    = ('userpass', @$args{qw(socks_user socks_pass)})
+    if $args->{socks_user};
+  my $reactor = $self->reactor;
+  $reactor->remove($handle);
+  return $self->emit(error => 'SOCKS upgrade failed')
+    unless IO::Socket::Socks->start_SOCKS($handle, %options);
+  weaken $self;
+  $reactor->io($handle => sub { $self->_socks($args) })->watch($handle, 0, 1);
+}
 
-  # Disable Nagle's algorithm
-  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+sub _try_tls {
+  my ($self, $args) = @_;
 
+  my $handle = $self->{handle};
   return $self->_cleanup->emit_safe(connect => $handle)
     if !$args->{tls} || $handle->isa('IO::Socket::SSL');
   return $self->emit(error => 'IO::Socket::SSL 1.84 required for TLS support')
@@ -104,7 +155,7 @@ sub _try {
     SSL_ca_file => $args->{tls_ca}
       && -T $args->{tls_ca} ? $args->{tls_ca} : undef,
     SSL_cert_file  => $args->{tls_cert},
-    SSL_error_trap => sub { $self->_cleanup->emit(error => $_[1]) },
+    SSL_error_trap => sub { $self->emit(error => $_[1]) },
     SSL_hostname   => IO::Socket::SSL->can_client_sni ? $args->{address} : '',
     SSL_key_file   => $args->{tls_key},
     SSL_startHandshake  => 0,
@@ -115,7 +166,7 @@ sub _try {
   my $reactor = $self->reactor;
   $reactor->remove($handle);
   return $self->emit(error => 'TLS upgrade failed')
-    unless $handle = IO::Socket::SSL->start_SSL($handle, %options);
+    unless IO::Socket::SSL->start_SSL($handle, %options);
   $reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1);
 }
 
@@ -225,6 +276,30 @@ Local address to bind to.
 
 Port to connect to, defaults to C<80> or C<443> with C<tls> option.
 
+=item socks_address
+
+  socks_address => '127.0.0.1'
+
+Address or host name of SOCKS5 proxy server to use for connection.
+
+=item socks_pass
+
+  socks_pass => 'secr3t'
+
+Password to use for SOCKS5 authentication.
+
+=item socks_port
+
+  socks_port => 9050
+
+Port of SOCKS5 proxy server to use for connection.
+
+=item socks_user
+
+  socks_user => 'sri'
+
+Username to use for SOCKS5 authentication.
+
 =item timeout
 
   timeout => 15
@@ -1,8 +1,8 @@
 package Mojo::IOLoop::Delay;
 use Mojo::Base 'Mojo::EventEmitter';
 
-use Mojo;
 use Mojo::IOLoop;
+use Mojo::Util;
 use Hash::Util::FieldHash 'fieldhash';
 
 has ioloop => sub { Mojo::IOLoop->singleton };
@@ -16,7 +16,7 @@ sub begin {
   return sub { $self->_step($id, $offset // 1, $len, @_) };
 }
 
-sub data { shift->Mojo::_dict(data => @_) }
+sub data { Mojo::Util::_stash(data => @_) }
 
 sub pass { $_[0]->begin->(@_) }
 
@@ -320,11 +320,12 @@ right in, to make writing test servers as easy as possible. Also note that for
 convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop>
 is loaded.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
-Individual features can also be disabled with the C<MOJO_NO_IPV6> and
-C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
 
@@ -1,8 +1,6 @@
 package Mojo::JSON::Pointer;
 use Mojo::Base -base;
 
-use Scalar::Util 'looks_like_number';
-
 has 'data';
 
 sub contains { shift->_pointer(1, @_) }
@@ -23,7 +21,7 @@ sub _pointer {
     if (ref $data eq 'HASH' && exists $data->{$p}) { $data = $data->{$p} }
 
     # Array
-    elsif (ref $data eq 'ARRAY' && looks_like_number($p) && @$data > $p) {
+    elsif (ref $data eq 'ARRAY' && $p =~ /^\d+$/ && @$data > $p) {
       $data = $data->[$p];
     }
 
@@ -218,8 +218,7 @@ sub _decode_value {
 }
 
 sub _encode_array {
-  my $array = shift;
-  return '[' . join(',', map { _encode_value($_) } @$array) . ']';
+  '[' . join(',', map { _encode_value($_) } @{$_[0]}) . ']';
 }
 
 sub _encode_object {
@@ -231,7 +230,7 @@ sub _encode_object {
 
 sub _encode_string {
   my $str = shift;
-  $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
+  $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"])!$REVERSE{$1}!gs;
   return "\"$str\"";
 }
 
@@ -55,7 +55,7 @@ sub _all {
   seek $handle, 0, 0;
   my $data = join '', <$handle>;
 
-  # Ignore everything before __DATA__ (Windows will seek to start of file)
+  # Ignore everything before __DATA__ (some versions seek to start of file)
   $data =~ s/^.*\n__DATA__\r?\n/\n/s;
 
   # Ignore everything after __END__
@@ -11,11 +11,9 @@ has url => sub { Mojo::URL->new };
 has 'reverse_proxy';
 
 my $START_LINE_RE = qr/
-  ^
-  ([a-zA-Z]+)                                               # Method
+  ^([a-zA-Z]+)                                              # Method
   \s+([0-9a-zA-Z!#\$\%&'()*+,\-.\/:;=?\@[\\\]^_`\{|\}~]+)   # URL
-  \s+HTTP\/(\d\.\d)                                         # Version
-  $
+  \s+HTTP\/(\d\.\d)$                                        # Version
 /x;
 
 sub clone {
@@ -60,8 +58,7 @@ sub extract_start_line {
   return undef unless $$bufref =~ s/^\s*(.*?)\x0d?\x0a//;
 
   # We have a (hopefully) full request line
-  $self->error({message => 'Bad request start line', advice => 400})
-    and return undef
+  return !$self->error({message => 'Bad request start line', advice => 400})
     unless $1 =~ $START_LINE_RE;
   my $url = $self->method($1)->version($3)->url;
   return !!($1 eq 'CONNECT' ? $url->authority($2) : $url->parse($2));
@@ -107,9 +104,8 @@ sub get_start_line_chunk {
     }
 
     # Proxy
-    elsif ($self->proxy) {
-      $path = $url->clone->userinfo(undef)
-        unless $self->is_handshake || $url->protocol eq 'https';
+    elsif ($self->proxy && $url->protocol ne 'https') {
+      $path = $url->clone->userinfo(undef) unless $self->is_handshake;
     }
 
     $self->{start_buffer} = "$method $path HTTP/@{[$self->version]}\x0d\x0a";
@@ -225,10 +221,8 @@ sub _parse_env {
   $self->method($env->{REQUEST_METHOD}) if $env->{REQUEST_METHOD};
 
   # Scheme/Version
-  if (($env->{SERVER_PROTOCOL} // '') =~ m!^([^/]+)/([^/]+)$!) {
-    $base->scheme($1);
-    $self->version($2);
-  }
+  $base->scheme($1) and $self->version($2)
+    if ($env->{SERVER_PROTOCOL} // '') =~ m!^([^/]+)/([^/]+)$!;
 
   # HTTPS
   $base->scheme('https') if uc($env->{HTTPS} // '') eq 'ON';
@@ -400,21 +394,24 @@ Check C<X-Requested-With> header for C<XMLHttpRequest> value.
   my @foo         = $req->param('foo');
   my ($foo, $bar) = $req->param(['foo', 'bar']);
 
-Access C<GET> and C<POST> parameters. Note that this method caches all data,
-so it should not be called before the entire request body has been received.
-Parts of the request body need to be loaded into memory to parse C<POST>
-parameters, so you have to make sure it is not excessively large, there's a
-10MB limit by default.
+Access C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body.
+Note that this method caches all data, so it should not be called before the
+entire request body has been received. Parts of the request body need to be
+loaded into memory to parse C<POST> parameters, so you have to make sure it is
+not excessively large, there's a 10MB limit by default.
 
 =head2 params
 
   my $params = $req->params;
 
-All C<GET> and C<POST> parameters, usually a L<Mojo::Parameters> object. Note
-that this method caches all data, so it should not be called before the entire
-request body has been received. Parts of the request body need to be loaded
-into memory to parse C<POST> parameters, so you have to make sure it is not
-excessively large, there's a 10MB limit by default.
+All C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body,
+usually a L<Mojo::Parameters> object. Note that this method caches all data,
+so it should not be called before the entire request body has been received.
+Parts of the request body need to be loaded into memory to parse C<POST>
+parameters, so you have to make sure it is not excessively large, there's a
+10MB limit by default.
 
   # Get parameter value
   say $req->params->param('foo');
@@ -56,7 +56,6 @@ my %MESSAGES = (
   428 => 'Precondition Required',              # RFC 6585
   429 => 'Too Many Requests',                  # RFC 6585
   431 => 'Request Header Fields Too Large',    # RFC 6585
-  451 => 'Unavailable For Legal Reasons',      # Draft
   500 => 'Internal Server Error',
   501 => 'Not Implemented',
   502 => 'Bad Gateway',
@@ -95,7 +94,7 @@ sub extract_start_line {
 
   # We have a full response line
   return undef unless $$bufref =~ s/^(.*?)\x0d?\x0a//;
-  $self->error({message => 'Bad response start line'}) and return undef
+  return !$self->error({message => 'Bad response start line'})
     unless $1 =~ m!^\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?$!;
 
   my $content = $self->content;
@@ -9,7 +9,7 @@ use Mojo::JSON 'j';
 use Mojo::JSON::Pointer;
 use Mojo::Parameters;
 use Mojo::Upload;
-use Mojo::Util 'decode';
+use Mojo::Util qw(decode split_header);
 
 has content => sub { Mojo::Content::Single->new };
 has default_charset  => 'UTF-8';
@@ -585,11 +585,12 @@ sure it is not excessively large, there's a 10MB limit by default.
   my @foo         = $msg->param('foo');
   my ($foo, $bar) = $msg->param(['foo', 'bar']);
 
-Access C<POST> parameters. Note that this method caches all data, so it should
-not be called before the entire message body has been received. Parts of the
-message body need to be loaded into memory to parse C<POST> parameters, so you
-have to make sure it is not excessively large, there's a 10MB limit by
-default.
+Access C<POST> parameters extracted from C<application/x-www-form-urlencoded>
+or C<multipart/form-data> message body. Note that this method caches all data,
+so it should not be called before the entire message body has been received.
+Parts of the message body need to be loaded into memory to parse C<POST>
+parameters, so you have to make sure it is not excessively large, there's a
+10MB limit by default.
 
 =head2 parse
 
@@ -31,9 +31,10 @@ sub append {
 sub clone {
   my $self = shift;
 
-  my $clone = $self->new->charset($self->charset);
-  if (defined $self->{string}) { $clone->{string} = $self->{string} }
-  else                         { $clone->params([@{$self->params}]) }
+  my $clone = $self->new;
+  if   (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
+  if   (defined $self->{string}) { $clone->{string}  = $self->{string} }
+  else                           { $clone->{params}  = [@{$self->params}] }
 
   return $clone;
 }
@@ -32,7 +32,8 @@ sub canonicalize {
 sub clone {
   my $self = shift;
 
-  my $clone = $self->new->charset($self->charset);
+  my $clone = $self->new;
+  if (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
   if (my $parts = $self->{parts}) {
     $clone->{$_} = $self->{$_} for qw(leading_slash trailing_slash);
     $clone->{parts} = [@$parts];
@@ -2,7 +2,7 @@ package Mojo::Reactor;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Carp 'croak';
-use IO::Poll qw(POLLERR POLLHUP POLLIN POLLPRI);
+use IO::Poll qw(POLLIN POLLPRI);
 use Mojo::Loader;
 
 sub again { croak 'Method "again" not implemented by subclass' }
@@ -14,16 +14,9 @@ sub detect {
 
 sub io { croak 'Method "io" not implemented by subclass' }
 
+# This may break at some point in the future, but is worth it for performance
 sub is_readable {
-  my ($self, $handle) = @_;
-
-  my $test = $self->{test} ||= IO::Poll->new;
-  $test->mask($handle, POLLIN | POLLPRI);
-  $test->poll(0);
-  my $result = $test->handles(POLLIN | POLLPRI | POLLERR | POLLHUP);
-  $test->remove($handle);
-
-  return !!$result;
+  !(IO::Poll::_poll(0, fileno(pop), my $dummy = POLLIN | POLLPRI) == 0);
 }
 
 sub is_running { croak 'Method "is_running" not implemented by subclass' }
@@ -26,18 +26,17 @@ sub run {
   # Response start line
   STDOUT->autoflush(1);
   binmode STDOUT;
-  my $res = $tx->res;
+  my $res = $tx->res->fix_headers;
   return undef if $self->nph && !_write($res, 'get_start_line_chunk');
 
   # Response headers
-  $res->fix_headers;
   my $code = $res->code    || 404;
   my $msg  = $res->message || $res->default_message;
   $res->headers->status("$code $msg") unless $self->nph;
   return undef unless _write($res, 'get_header_chunk');
 
   # Response body
-  $tx->is_empty or _write($res, 'get_body_chunk') or return undef;
+  return undef unless $tx->is_empty || _write($res, 'get_body_chunk');
 
   # Finish transaction
   $tx->server_close;
@@ -261,15 +261,15 @@ Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server
 =head1 DESCRIPTION
 
 L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O
-HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive,
-connection pooling, timeout, cookie, multipart and multiple event loop
-support.
-
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive
+and multiple event loop support.
+
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -152,10 +152,9 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD!
 L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, preforking
 non-blocking I/O HTTP and WebSocket server, built around the very well tested
 and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, Comet (long polling),
-keep-alive, connection pooling, timeout, cookie, multipart, multiple event
-loop and hot deployment support that just works. Note that the server uses
-signals for process management, so you should avoid modifying signal handlers
-in your applications.
+keep-alive, multiple event loop and hot deployment support that just works.
+Note that the server uses signals for process management, so you should avoid
+modifying signal handlers in your applications.
 
 To start applications with it you can use the L<hypnotoad> script, for
 L<Mojolicious> and L<Mojolicious::Lite> applications it will default to
@@ -172,11 +171,12 @@ You can run the same command again for automatic hot deployment.
 This second invocation will load the application again, detect the process id
 file with it, and send a L</"USR2"> signal to the already running server.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -120,21 +120,22 @@ Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM!
 
 L<Mojo::Server::Morbo> is a full featured, self-restart capable non-blocking
 I/O HTTP and WebSocket server, built around the very well tested and reliable
-L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), keep-alive,
-connection pooling, timeout, cookie, multipart and multiple event loop
-support. Note that the server uses signals for process management, so you
-should avoid modifying signal handlers in your applications.
+L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), keep-alive and
+multiple event loop support. Note that the server uses signals for process
+management, so you should avoid modifying signal handlers in your
+applications.
 
 To start applications with it you can use the L<morbo> script.
 
   $ morbo myapp.pl
   Server available at http://127.0.0.1:3000.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -21,10 +21,9 @@ sub run {
   $self->emit(request => $tx);
 
   # Response headers
-  my $res     = $tx->res->fix_headers;
-  my $headers = $res->headers;
+  my $res  = $tx->res->fix_headers;
+  my $hash = $res->headers->to_hash(1);
   my @headers;
-  my $hash = $headers->to_hash(1);
   for my $name (keys %$hash) {
     push @headers, map { $name => $_ } @{$hash->{$name}};
   }
@@ -257,15 +257,16 @@ Mojo::Server::Prefork - Preforking non-blocking I/O HTTP and WebSocket server
 L<Mojo::Server::Prefork> is a full featured, UNIX optimized, preforking
 non-blocking I/O HTTP and WebSocket server, built around the very well tested
 and reliable L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling),
-keep-alive, connection pooling, timeout, cookie, multipart and multiple event
-loop support. Note that the server uses signals for process management, so you
-should avoid modifying signal handlers in your applications.
-
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+keep-alive and multiple event loop support. Note that the server uses signals
+for process management, so you should avoid modifying signal handlers in your
+applications.
+
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -50,12 +50,8 @@ sub load_app {
     local $ENV{MOJO_EXE};
 
     # Try to load application from script into sandbox
-    my $app = eval sprintf <<'EOF', md5_sum($path . $$);
-package Mojo::Server::SandBox::%s;
-my $app = do $path;
-if (!$app && (my $e = $@ || $!)) { die $e }
-$app;
-EOF
+    my $app = eval "package Mojo::Server::Sandbox::@{[md5_sum $path]};"
+      . 'return do($path) || die($@ || $!);';
     die qq{Couldn't load application from file "$path": $@} if !$app && $@;
     die qq{File "$path" did not return an application object.\n}
       unless blessed $app && $app->isa('Mojo');
@@ -18,12 +18,12 @@ use constant GUID => '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
 
 # Opcodes
 use constant {
-  CONTINUATION => 0,
-  TEXT         => 1,
-  BINARY       => 2,
-  CLOSE        => 8,
-  PING         => 9,
-  PONG         => 10
+  CONTINUATION => 0x0,
+  TEXT         => 0x1,
+  BINARY       => 0x2,
+  CLOSE        => 0x8,
+  PING         => 0x9,
+  PONG         => 0xa
 };
 
 has [qw(compressed masked)];
@@ -35,35 +35,30 @@ sub build_frame {
   warn "-- Building frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
 
   # Head
-  my $frame = 0b00000000;
-  vec($frame, 0, 8) = $op | 0b10000000 if $fin;
-  vec($frame, 0, 8) |= 0b01000000 if $rsv1;
-  vec($frame, 0, 8) |= 0b00100000 if $rsv2;
-  vec($frame, 0, 8) |= 0b00010000 if $rsv3;
+  my $head = $op + ($fin ? 128 : 0);
+  $head |= 0b01000000 if $rsv1;
+  $head |= 0b00100000 if $rsv2;
+  $head |= 0b00010000 if $rsv3;
+  my $frame = pack 'C', $head;
 
   # Small payload
   my $len    = length $payload;
-  my $prefix = 0;
   my $masked = $self->masked;
   if ($len < 126) {
     warn "-- Small payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? ($len | 0b10000000) : $len;
-    $frame .= $prefix;
+    $frame .= pack 'C', $masked ? ($len | 128) : $len;
   }
 
   # Extended payload (16bit)
   elsif ($len < 65536) {
     warn "-- Extended 16bit payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? (126 | 0b10000000) : 126;
-    $frame .= $prefix;
-    $frame .= pack 'n', $len;
+    $frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
   }
 
   # Extended payload (64bit with 32bit fallback)
   else {
     warn "-- Extended 64bit payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? (127 | 0b10000000) : 127;
-    $frame .= $prefix;
+    $frame .= pack 'C', $masked ? (127 | 128) : 127;
     $frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
   }
 
@@ -160,39 +155,38 @@ sub parse_frame {
   my ($self, $buffer) = @_;
 
   # Head
-  return undef unless length(my $clone = $$buffer) >= 2;
-  my $head = substr $clone, 0, 2;
+  return undef unless length $$buffer >= 2;
+  my ($first, $second) = unpack 'C*', substr($$buffer, 0, 2);
 
   # FIN
-  my $fin = (vec($head, 0, 8) & 0b10000000) == 0b10000000 ? 1 : 0;
+  my $fin = ($first & 0b10000000) == 0b10000000 ? 1 : 0;
 
   # RSV1-3
-  my $rsv1 = (vec($head, 0, 8) & 0b01000000) == 0b01000000 ? 1 : 0;
-  my $rsv2 = (vec($head, 0, 8) & 0b00100000) == 0b00100000 ? 1 : 0;
-  my $rsv3 = (vec($head, 0, 8) & 0b00010000) == 0b00010000 ? 1 : 0;
+  my $rsv1 = ($first & 0b01000000) == 0b01000000 ? 1 : 0;
+  my $rsv2 = ($first & 0b00100000) == 0b00100000 ? 1 : 0;
+  my $rsv3 = ($first & 0b00010000) == 0b00010000 ? 1 : 0;
 
   # Opcode
-  my $op = vec($head, 0, 8) & 0b00001111;
+  my $op = $first & 0b00001111;
   warn "-- Parsing frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
 
   # Small payload
-  my $len = vec($head, 1, 8) & 0b01111111;
-  my $hlen = 2;
+  my ($hlen, $len) = (2, $second & 0b01111111);
   if ($len < 126) { warn "-- Small payload ($len)\n" if DEBUG }
 
   # Extended payload (16bit)
   elsif ($len == 126) {
-    return undef unless length $clone > 4;
+    return undef unless length $$buffer > 4;
     $hlen = 4;
-    $len = unpack 'n', substr($clone, 2, 2);
+    $len = unpack 'n', substr($$buffer, 2, 2);
     warn "-- Extended 16bit payload ($len)\n" if DEBUG;
   }
 
   # Extended payload (64bit with 32bit fallback)
   elsif ($len == 127) {
-    return undef unless length $clone > 10;
+    return undef unless length $$buffer > 10;
     $hlen = 10;
-    my $ext = substr $clone, 2, 8;
+    my $ext = substr $$buffer, 2, 8;
     $len = MODERN ? unpack('Q>', $ext) : unpack('N', substr($ext, 4, 4));
     warn "-- Extended 64bit payload ($len)\n" if DEBUG;
   }
@@ -201,19 +195,14 @@ sub parse_frame {
   $self->finish(1009) and return undef if $len > $self->max_websocket_size;
 
   # Check if whole packet has arrived
-  my $masked = vec($head, 1, 8) & 0b10000000;
-  return undef if length $clone < ($len + $hlen + ($masked ? 4 : 0));
-  substr $clone, 0, $hlen, '';
+  $len += 4 if my $masked = $second & 0b10000000;
+  return undef if length $$buffer < ($hlen + $len);
+  substr $$buffer, 0, $hlen, '';
 
   # Payload
-  $len += 4 if $masked;
-  return undef if length $clone < $len;
-  my $payload = $len ? substr($clone, 0, $len, '') : '';
-
-  # Unmask payload
+  my $payload = $len ? substr($$buffer, 0, $len, '') : '';
   $payload = xor_encode($payload, substr($payload, 0, 4, '') x 128) if $masked;
   warn "$payload\n" if DEBUG;
-  $$buffer = $clone;
 
   return [$fin, $rsv1, $rsv2, $rsv3, $op, $payload];
 }
@@ -41,13 +41,10 @@ sub host_port {
 }
 
 sub clone {
-  my $self = shift;
-
+  my $self  = shift;
   my $clone = $self->new;
-  $clone->$_($self->$_) for qw(scheme userinfo host port fragment);
-  $clone->path($self->path->clone)->query($self->query->clone);
-  $clone->base($self->base->clone) if $self->{base};
-
+  @$clone{keys %$self} = values %$self;
+  $clone->{$_} && ($clone->{$_} = $clone->{$_}->clone) for qw(base path query);
   return $clone;
 }
 
@@ -58,22 +58,22 @@ L<Mojo::UserAgent::Proxy> implements the following attributes.
 
 =head2 http
 
-  my $http = $ua->http;
-  $ua      = $ua->http('http://sri:secret@127.0.0.1:8080');
+  my $http = $proxy->http;
+  $proxy   = $proxy->http('socks://sri:secret@127.0.0.1:8080');
 
 Proxy server to use for HTTP and WebSocket requests.
 
 =head2 https
 
-  my $https = $ua->https;
-  $ua       = $ua->https('http://sri:secret@127.0.0.1:8080');
+  my $https = $proxy->https;
+  $proxy    = $proxy->https('http://sri:secret@127.0.0.1:8080');
 
 Proxy server to use for HTTPS and WebSocket requests.
 
 =head2 not
 
   my $not = $proxy->not;
-  $ua     = $proxy->not([qw(localhost intranet.mojolicio.us)]);
+  $proxy  = $proxy->not([qw(localhost intranet.mojolicio.us)]);
 
 Domains that don't require a proxy server to be used.
 
@@ -33,16 +33,15 @@ sub endpoint {
   my $port  = $url->port || ($proto eq 'https' ? 443 : 80);
 
   # Proxy for normal HTTP requests
+  my $socks;
+  if (my $proxy = $req->proxy) { $socks = $proxy->protocol eq 'socks' }
   return $self->_proxy($tx, $proto, $host, $port)
-    if $proto eq 'http' && !$req->is_handshake;
+    if $proto eq 'http' && !$req->is_handshake && !$socks;
 
   return $proto, $host, $port;
 }
 
-sub peer {
-  my ($self, $tx) = @_;
-  return $self->_proxy($tx, $self->endpoint($tx));
-}
+sub peer { $_[0]->_proxy($_[1], $_[0]->endpoint($_[1])) }
 
 sub proxy_connect {
   my ($self, $old) = @_;
@@ -53,6 +52,7 @@ sub proxy_connect {
 
   # No proxy
   return undef unless my $proxy = $req->proxy;
+  return undef if $proxy->protocol eq 'socks';
 
   # WebSocket and/or HTTPS
   my $url = $req->url;
@@ -284,6 +284,23 @@ Mojo::UserAgent::Transactor - User agent transactor
 L<Mojo::UserAgent::Transactor> is the transaction building and manipulation
 framework used by L<Mojo::UserAgent>.
 
+=head1 GENERATORS
+
+These content generators are available by default.
+
+=head2 form
+
+  $t->tx(POST => 'http://example.com' => form => {a => 'b'});
+
+Generate query string, C<application/x-www-form-urlencoded> or
+C<multipart/form-data> content.
+
+=head2 json
+
+  $t->tx(PATCH => 'http://example.com' => json => {a => 'b'});
+
+Generate JSON content with L<Mojo::JSON>.
+
 =head1 ATTRIBUTES
 
 L<Mojo::UserAgent::Transactor> implements the following attributes.
@@ -345,18 +362,18 @@ C<307> or C<308> redirect response if possible.
 
   my $tx = $t->tx(GET  => 'example.com');
   my $tx = $t->tx(POST => 'http://example.com');
-  my $tx = $t->tx(GET  => 'http://example.com' => {DNT => 1});
+  my $tx = $t->tx(GET  => 'http://example.com' => {Accept => '*/*'});
   my $tx = $t->tx(PUT  => 'http://example.com' => 'Hi!');
   my $tx = $t->tx(PUT  => 'http://example.com' => form => {a => 'b'});
   my $tx = $t->tx(PUT  => 'http://example.com' => json => {a => 'b'});
-  my $tx = $t->tx(POST => 'http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $t->tx(POST => 'http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $t->tx(
-    PUT  => 'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    PUT  => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $t->tx(
-    PUT  => 'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    PUT  => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Versatile general purpose L<Mojo::Transaction::HTTP> transaction builder for
-requests, with support for content generators.
+requests, with support for L</"GENERATORS">.
 
   # Generate and inspect custom GET request with DNT header and content
   say $t->tx(GET => 'example.com' => {DNT => 1} => 'Bye!')->req->to_string;
@@ -85,21 +85,33 @@ sub _cleanup {
 }
 
 sub _connect {
-  my ($self, $nb, $proto, $host, $port, $handle, $cb) = @_;
+  my ($self, $nb, $peer, $tx, $handle, $cb) = @_;
+
+  my $t = $self->transactor;
+  my ($proto, $host, $port) = $peer ? $t->peer($tx) : $t->endpoint($tx);
+  my %options
+    = (address => $host, port => $port, timeout => $self->connect_timeout);
+  if (my $local = $self->local_address) { $options{local_address} = $local }
+  $options{handle} = $handle if $handle;
+
+  # SOCKS
+  if ($proto eq 'socks') {
+    @options{qw(socks_address socks_port)} = @options{qw(address port)};
+    ($proto, @options{qw(address port)}) = $t->endpoint($tx);
+    my $req      = $tx->req;
+    my $userinfo = $req->proxy->userinfo;
+    $req->proxy(0);
+    @options{qw(socks_user socks_pass)} = split ':', $userinfo if $userinfo;
+  }
+
+  # TLS
+  map { $options{"tls_$_"} = $self->$_ } qw(ca cert key)
+    if ($options{tls} = $proto eq 'https');
 
   weaken $self;
   my $id;
   return $id = $self->_loop($nb)->client(
-    address       => $host,
-    handle        => $handle,
-    local_address => $self->local_address,
-    port          => $port,
-    timeout       => $self->connect_timeout,
-    tls           => $proto eq 'https',
-    tls_ca        => $self->ca,
-    tls_cert      => $self->cert,
-    tls_key       => $self->key,
-    sub {
+    %options => sub {
       my ($loop, $err, $stream) = @_;
 
       # Connection error
@@ -127,12 +139,11 @@ sub _connect_proxy {
       my ($self, $tx) = @_;
 
       # CONNECT failed (connection needs to be kept alive)
-      unless ($tx->keep_alive && $tx->res->is_status_class(200)) {
-        $old->req->error({message => 'Proxy connection failed'});
-        return $self->$cb($old);
-      }
+      $old->res->error({message => 'Proxy connection failed'})
+        and return $self->$cb($old)
+        if $tx->error || !$tx->res->is_status_class(200) || !$tx->keep_alive;
 
-      # Prevent proxy reassignment and start real transaction
+      # Start real transaction
       $old->req->proxy(0);
       my $id = $tx->connection;
       return $self->_start($nb, $old->connection($id), $cb)
@@ -141,11 +152,10 @@ sub _connect_proxy {
       # TLS upgrade
       my $loop   = $self->_loop($nb);
       my $handle = $loop->stream($id)->steal_handle;
-      my $c      = delete $self->{connections}{$id};
       $loop->remove($id);
-      $id = $self->_connect($nb, $self->transactor->endpoint($old),
-        $handle, sub { shift->_start($nb, $old->connection($id), $cb) });
-      $self->{connections}{$id} = $c;
+      $id = $self->_connect($nb, 0, $old, $handle,
+        sub { shift->_start($nb, $old->connection($id), $cb) });
+      $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $old};
     }
   );
 }
@@ -190,8 +200,7 @@ sub _connection {
 
   # Connect
   warn "-- Connect ($proto:$host:$port)\n" if DEBUG;
-  ($proto, $host, $port) = $self->transactor->peer($tx);
-  $id = $self->_connect(($nb, $proto, $host, $port, $id) => \&_connected);
+  $id = $self->_connect($nb, 1, $tx, $id, \&_connected);
   $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $tx};
 
   return $id;
@@ -239,7 +248,7 @@ sub _finish {
   my ($self, $id, $close) = @_;
 
   # Remove request timeout
-  my $c = $self->{connections}{$id};
+  return unless my $c    = $self->{connections}{$id};
   return unless my $loop = $self->_loop($c->{nb});
   $loop->remove($c->{timeout}) if $c->{timeout};
 
@@ -293,11 +302,8 @@ sub _remove {
   # Close connection
   my $c = delete $self->{connections}{$id} || {};
   my $tx = $c->{tx};
-  if ($close || !$tx || !$tx->keep_alive || $tx->error) {
-    $self->_dequeue($_, $id) for 1, 0;
-    $self->_loop($_)->remove($id) for 1, 0;
-    return;
-  }
+  return map { $self->_dequeue($_, $id); $self->_loop($_)->remove($id) } 1, 0
+    if $close || !$tx || !$tx->keep_alive || $tx->error;
 
   # Keep connection alive (CONNECT requests get upgraded)
   $self->_enqueue($c->{nb}, join(':', $self->transactor->endpoint($tx)), $id)
@@ -424,19 +430,20 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
 =head1 DESCRIPTION
 
 L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user
-agent, with IPv6, TLS, SNI, IDNA, Comet (long polling), keep-alive, connection
-pooling, timeout, cookie, multipart, proxy, gzip compression and multiple
-event loop support.
+agent, with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling),
+keep-alive, connection pooling, timeout, cookie, multipart, gzip compression
+and multiple event loop support.
 
 All connections will be reset automatically if a new process has been forked,
 this allows multiple processes to share the same L<Mojo::UserAgent> object
 safely.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
+L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
+automatically if they are installed. Individual features can also be disabled
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
+variables.
 
 See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
 
@@ -580,6 +587,12 @@ Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> object.
   # Detect proxy servers from environment
   $ua->proxy->detect;
 
+  # Manually configure HTTP proxy (using CONNECT for HTTPS)
+  $ua->proxy->http('http://127.0.0.1:8080')->https('http://127.0.0.1:8080');
+
+  # Manually configure Tor (SOCKS5)
+  $ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050');
+
 =head2 request_timeout
 
   my $timeout = $ua->request_timeout;
@@ -632,11 +645,12 @@ implements the following new ones.
 =head2 build_tx
 
   my $tx = $ua->build_tx(GET => 'example.com');
-  my $tx = $ua->build_tx(PUT => 'http://example.com' => {DNT => 1} => 'Hi!');
   my $tx = $ua->build_tx(
-    PUT => 'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    PUT => 'http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->build_tx(
+    PUT => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->build_tx(
-    PUT => 'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    PUT => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Generate L<Mojo::Transaction::HTTP> object with
 L<Mojo::UserAgent::Transactor/"tx">.
@@ -672,16 +686,16 @@ L<Mojo::UserAgent::Transactor/"websocket">.
 =head2 delete
 
   my $tx = $ua->delete('example.com');
-  my $tx = $ua->delete('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->delete('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->delete(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->delete(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<DELETE> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<DELETE> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->delete('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -692,14 +706,16 @@ append a callback to perform requests non-blocking.
 =head2 get
 
   my $tx = $ua->get('example.com');
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => 'Hi!');
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => form => {a => 'b'});
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => json => {a => 'b'});
+  my $tx = $ua->get('http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<GET> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<GET> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->get('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -710,16 +726,16 @@ append a callback to perform requests non-blocking.
 =head2 head
 
   my $tx = $ua->head('example.com');
-  my $tx = $ua->head('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->head('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->head(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->head(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<HEAD> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<HEAD> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->head('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -730,16 +746,16 @@ append a callback to perform requests non-blocking.
 =head2 options
 
   my $tx = $ua->options('example.com');
-  my $tx = $ua->options('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->options('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->options(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->options(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<OPTIONS> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<OPTIONS> method, which
+is implied). You can also append a callback to perform requests non-blocking.
 
   $ua->options('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -750,16 +766,16 @@ append a callback to perform requests non-blocking.
 =head2 patch
 
   my $tx = $ua->patch('example.com');
-  my $tx = $ua->patch('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->patch('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->patch(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->patch(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<PATCH> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PATCH> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->patch('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -770,16 +786,16 @@ append a callback to perform requests non-blocking.
 =head2 post
 
   my $tx = $ua->post('example.com');
-  my $tx = $ua->post('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->post('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->post(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->post(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<POST> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<POST> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->post('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -790,14 +806,16 @@ append a callback to perform requests non-blocking.
 =head2 put
 
   my $tx = $ua->put('example.com');
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => 'Hi!');
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => form => {a => 'b'});
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => json => {a => 'b'});
+  my $tx = $ua->put('http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<PUT> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PUT> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->put('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -11,6 +11,7 @@ use File::Basename 'dirname';
 use File::Spec::Functions 'catfile';
 use List::Util 'min';
 use MIME::Base64 qw(decode_base64 encode_base64);
+use Symbol 'delete_package';
 use Time::HiRes ();
 
 # Check for monotonic clock support
@@ -327,8 +328,8 @@ sub unquote {
 
 sub url_escape {
   my ($str, $pattern) = @_;
-  $pattern ||= '^A-Za-z0-9\-._~';
-  $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge;
+  if ($pattern) { $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge }
+  else          { $str =~ s/([^A-Za-z0-9\-._~])/sprintf('%%%02X',ord($1))/ge }
   return $str;
 }
 
@@ -389,6 +390,44 @@ sub _encoding {
   $CACHE{$_[0]} //= find_encoding($_[0]) // croak "Unknown encoding '$_[0]'";
 }
 
+sub _options {
+
+  # Hash or name (one)
+  return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
+
+  # Name and values (odd)
+  return shift, @_ if @_ % 2;
+
+  # Name and hash or just values (even)
+  return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
+}
+
+sub _stash {
+  my ($name, $object) = (shift, shift);
+
+  # Hash
+  my $dict = $object->{$name} ||= {};
+  return $dict unless @_;
+
+  # Get
+  return $dict->{$_[0]} unless @_ > 1 || ref $_[0];
+
+  # Set
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$dict{keys %$values} = values %$values;
+
+  return $object;
+}
+
+sub _teardown {
+  return unless my $class = shift;
+
+  # @ISA has to be cleared first because of circular references
+  no strict 'refs';
+  @{"${class}::ISA"} = ();
+  delete_package $class;
+}
+
 1;
 
 =encoding utf8
@@ -8,6 +8,7 @@ use Mojo::Home;
 use Mojo::Log;
 use Mojo::Transaction::HTTP;
 use Mojo::UserAgent;
+use Mojo::Util;
 use Scalar::Util 'weaken';
 
 has home => sub { Mojo::Home->new };
@@ -23,7 +24,7 @@ has ua   => sub {
 
 sub build_tx { Mojo::Transaction::HTTP->new }
 
-sub config { shift->_dict(config => @_) }
+sub config { Mojo::Util::_stash(config => @_) }
 
 sub handler { croak 'Method "handler" not implemented in subclass' }
 
@@ -39,22 +40,6 @@ sub new {
   return $self;
 }
 
-sub _dict {
-  my ($self, $name) = (shift, shift);
-
-  # Hash
-  my $dict = $self->{$name} ||= {};
-  return $dict unless @_;
-
-  # Get
-  return $dict->{$_[0]} unless @_ > 1 || ref $_[0];
-
-  # Set
-  %$dict = (%$dict, %{ref $_[0] ? $_[0] : {@_}});
-
-  return $self;
-}
-
 1;
 
 =encoding utf8
@@ -19,14 +19,14 @@ EOF
   # Script
   my $name = class_to_file $class;
   $self->render_to_rel_file('mojo', "$name/script/$name", $class);
-  $self->chmod_file("$name/script/$name", 0744);
+  $self->chmod_rel_file("$name/script/$name", 0744);
 
   # Application class
   my $app = class_to_path $class;
   $self->render_to_rel_file('appclass', "$name/lib/$app", $class);
 
   # Controller
-  my $controller = "${class}::Example";
+  my $controller = "${class}::Controller::Example";
   my $path       = class_to_path $controller;
   $self->render_to_rel_file('controller', "$name/lib/$path", $controller);
 
@@ -8,7 +8,7 @@ sub run {
   my ($self, $name) = @_;
   $name ||= 'myapp.pl';
   $self->render_to_rel_file('liteapp', $name);
-  $self->chmod_file($name, 0744);
+  $self->chmod_rel_file($name, 0744);
 }
 
 1;
@@ -132,7 +132,7 @@ Mojolicious::Command::get - Get command
     ./myapp.pl get /
     mojo get mojolicio.us
     mojo get -v -r google.com
-    mojo get -v -H 'Host: mojolicious.org' -H 'DNT: 1' mojolicio.us
+    mojo get -v -H 'Host: mojolicious.org' -H 'Accept: */*' mojolicio.us
     mojo get -M POST -c 'trololo' mojolicio.us
     mojo get mojolicio.us 'head > title' text
     mojo get mojolicio.us .footer all
@@ -44,13 +44,10 @@ sub _walk {
 
   # Regex (verbose)
   my $pattern = $route->pattern;
-  $pattern->match('/', $route->is_endpoint);
+  $pattern->match('/', $route->is_endpoint && !$partial);
   my $regex = (regexp_pattern $pattern->regex)[0];
   my $format = (regexp_pattern($pattern->format_regex || ''))[0];
-  my $optional
-    = !$pattern->constraints->{format} || $pattern->defaults->{format};
-  $regex .= $optional ? "(?:$format)?" : $format if $format && !$partial;
-  push @$row, $regex if $verbose;
+  push @$row, $regex, $format ? $format : '' if $verbose;
 
   $depth++;
   _walk($_, $depth, $rows, $verbose) for @{$route->children};
@@ -1,11 +1,7 @@
 package Mojolicious::Command::test;
 use Mojo::Base 'Mojolicious::Command';
 
-use Cwd 'realpath';
-use FindBin;
-use File::Spec::Functions qw(abs2rel catdir splitdir);
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
-use Mojo::Home;
 
 has description => 'Run tests.';
 has usage => sub { shift->extract_usage };
@@ -15,19 +11,11 @@ sub run {
 
   GetOptionsFromArray \@args, 'v|verbose' => sub { $ENV{HARNESS_VERBOSE} = 1 };
 
-  unless (@args) {
-    my @base = splitdir(abs2rel $FindBin::Bin);
-
-    # "./t"
-    my $path = catdir @base, 't';
-
-    # "../t"
-    $path = catdir @base, '..', 't' unless -d $path;
-    die "Can't find test directory.\n" unless -d $path;
-
-    my $home = Mojo::Home->new($path);
-    /\.t$/ and push @args, $home->rel_file($_) for @{$home->list_files};
-    say "Running tests from '", realpath($path), "'.";
+  if (!@args && (my $home = $self->app->home)) {
+    die "Can't find test directory.\n" unless -d $home->rel_dir('t');
+    my $files = $home->list_files('t');
+    /\.t$/ and push @args, $home->rel_file("t/$_") for @$files;
+    say qq{Running tests from "}, $home->rel_dir('t') . '".';
   }
 
   $ENV{HARNESS_OPTIONS} //= 'c';
@@ -1,7 +1,7 @@
 package Mojolicious::Command::version;
 use Mojo::Base 'Mojolicious::Command';
 
-use Mojo::IOLoop::Server;
+use Mojo::IOLoop::Client;
 use Mojo::UserAgent;
 use Mojolicious;
 
@@ -11,11 +11,11 @@ has usage => sub { shift->extract_usage };
 sub run {
   my $self = shift;
 
-  my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed';
-  my $ipv6
-    = Mojo::IOLoop::Server::IPV6 ? $IO::Socket::IP::VERSION : 'not installed';
-  my $tls
-    = Mojo::IOLoop::Server::TLS ? $IO::Socket::SSL::VERSION : 'not installed';
+  my $ev    = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed';
+  my $class = 'Mojo::IOLoop::Client';
+  my $ipv6  = $class->IPV6 ? $IO::Socket::IP::VERSION : 'not installed';
+  my $socks = $class->SOCKS ? $IO::Socket::Socks::VERSION : 'not installed';
+  my $tls   = $class->TLS ? $IO::Socket::SSL::VERSION : 'not installed';
 
   print <<EOF;
 CORE
@@ -23,9 +23,10 @@ CORE
   Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME)
 
 OPTIONAL
-  EV 4.0+               ($ev)
-  IO::Socket::IP 0.20+  ($ipv6)
-  IO::Socket::SSL 1.84+ ($tls)
+  EV 4.0+                 ($ev)
+  IO::Socket::IP 0.20+    ($ipv6)
+  IO::Socket::Socks 0.64+ ($socks)
+  IO::Socket::SSL 1.84+   ($tls)
 
 EOF
 
@@ -20,8 +20,7 @@ has usage       => "Usage: APPLICATION\n";
 sub chmod_file {
   my ($self, $path, $mod) = @_;
   chmod $mod, $path or croak qq{Can't chmod file "$path": $!};
-  $mod = sprintf '%lo', $mod;
-  say "  [chmod] $path $mod" unless $self->quiet;
+  say "  [chmod] $path " . sprintf('%lo', $mod) unless $self->quiet;
   return $self;
 }
 
@@ -83,10 +83,7 @@ sub run {
   return print $self->message, tablify(\@rows), $self->hint;
 }
 
-sub start_app {
-  my $self = shift;
-  return Mojo::Server->new->build_app(shift)->start(@_);
-}
+sub start_app { shift; Mojo::Server->new->build_app(shift)->start(@_) }
 
 sub _command {
   my ($module, $fatal) = @_;
@@ -27,13 +27,13 @@ my %RESERVED = map { $_ => 1 } (
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   Carp::croak "Undefined subroutine &${package}::$method called"
     unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
   # Call helper with current controller
   Carp::croak qq{Can't locate object method "$method" via package "$package"}
-    unless my $helper = $self->app->renderer->helpers->{$method};
+    unless my $helper = $self->app->renderer->get_helper($method);
   return $self->$helper(@_);
 }
 
@@ -87,12 +87,14 @@ sub flash {
     if @_ == 1 && !ref $_[0];
 
   # Initialize new flash and merge values
-  my $flash = $session->{new_flash} ||= {};
-  %$flash = (%$flash, %{@_ > 1 ? {@_} : $_[0]});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @{$session->{new_flash} ||= {}}{keys %$values} = values %$values;
 
   return $self;
 }
 
+sub helpers { $_[0]->app->renderer->get_helper('')->($_[0]) }
+
 sub on {
   my ($self, $name, $cb) = @_;
   my $tx = $self->tx;
@@ -266,7 +268,8 @@ sub session {
   return $session->{$_[0]} unless @_ > 1 || ref $_[0];
 
   # Set
-  %$session = (%$session, %{ref $_[0] ? $_[0] : {@_}});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$session{keys %$values} = values %$values;
 
   return $self;
 }
@@ -311,7 +314,7 @@ sub signed_cookie {
   return wantarray ? @results : $results[0];
 }
 
-sub stash { shift->Mojolicious::_dict(stash => @_) }
+sub stash { Mojo::Util::_stash(stash => @_) }
 
 sub url_for {
   my $self = shift;
@@ -438,7 +441,7 @@ Mojolicious::Controller - Controller base class
 =head1 SYNOPSIS
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -553,6 +556,17 @@ L</"session">.
   $c->flash(message => 'User created successfully!');
   $c->redirect_to('show_user', id => 23);
 
+=head2 helpers
+
+  my $helpers = $c->helpers;
+
+Return a proxy object containing the current controller object and on which
+helpers provided by L</"app"> can be called. This includes all helpers from
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+  # Make sure to use the "title" helper and not the controller method
+  $c->helpers->title('Welcome!');
+
 =head2 on
 
   my $cb = $c->on(finish => sub {...});
@@ -598,12 +612,14 @@ status.
   $c              = $c->param(foo => ['ba;r', 'baz']);
 
 Access route placeholder values that are not reserved stash values, file
-uploads and C<GET>/C<POST> parameters, in that order. Note that this method is
-context sensitive in some cases and therefore needs to be used with care,
-there can always be multiple values, which might have unexpected consequences.
-Parts of the request body need to be loaded into memory to parse C<POST>
-parameters, so you have to make sure it is not excessively large, there's a
-10MB limit by default.
+uploads as well as C<GET> and C<POST> parameters extracted from the query
+string and C<application/x-www-form-urlencoded> or C<multipart/form-data>
+message body, in that order. Note that this method is context sensitive in
+some cases and therefore needs to be used with care, there can always be
+multiple values, which might have unexpected consequences. Parts of the
+request body need to be loaded into memory to parse C<POST> parameters, so you
+have to make sure it is not excessively large, there's a 10MB limit by
+default.
 
   # List context is ambiguous and should be avoided, you can get multiple
   # values returned for a query string like "?foo=bar&foo=baz&foo=yada"
@@ -612,7 +628,7 @@ parameters, so you have to make sure it is not excessively large, there's a
   # Better enforce scalar context
   my $hash = {foo => scalar $c->param('foo')};
 
-  # The multi-name form can also be used to enforce scalar context
+  # The multi-name form can also be used to enforce a list with one element
   my $hash = {foo => $c->param(['foo'])};
 
 For more control you can also access request information directly.
@@ -732,6 +748,10 @@ Render a static file using L<Mojolicious::Static/"serve">, usually from the
 C<public> directories or C<DATA> sections of your application. Note that this
 method does not protect from traversing to parent directories.
 
+  # Serve file with a custom content type
+  $c->res->headers->content_type('application/myapp');
+  $c->render_static('foo.txt');
+
 =head2 render_to_string
 
   my $output = $c->render_to_string('foo/index', format => 'pdf');
@@ -945,9 +965,11 @@ to inherit query parameters from the current request.
   my $validation = $c->validation;
 
 Get L<Mojolicious::Validator::Validation> object for current request to
-validate C<GET>/C<POST> parameters. Parts of the request body need to be
-loaded into memory to parse C<POST> parameters, so you have to make sure it is
-not excessively large, there's a 10MB limit by default.
+validate C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body.
+Parts of the request body need to be loaded into memory to parse C<POST>
+parameters, so you have to make sure it is not excessively large, there's a
+10MB limit by default.
 
   my $validation = $c->validation;
   $validation->required('title')->size(3, 50);
@@ -108,9 +108,9 @@ environments out of the box.
   $ hypnotoad script/myapp
   Server available at http://127.0.0.1:8080.
 
-You can tweak many configuration settings right from within your application
-with L<Mojo/"config">, for a full list see
-L<Mojo::Server::Hypnotoad/"SETTINGS">.
+It automatically sets the operating mode to C<production> and you can tweak
+many configuration settings right from within your application with
+L<Mojo/"config">, for a full list see L<Mojo::Server::Hypnotoad/"SETTINGS">.
 
   use Mojolicious::Lite;
 
@@ -1218,6 +1218,48 @@ the helper L<Mojolicious::Plugin::DefaultHelpers/"config">
 Alternatively you can also use configuration files in the JSON format with
 L<Mojolicious::Plugin::JSONConfig>.
 
+=head2 Adding a plugin to your application
+
+To organize your code better and to prevent helpers from cluttering your
+application, you can use application specific plugins.
+
+  $ mkdir -p lib/MyApp/Plugin
+  $ touch lib/MyApp/Plugin/MyHelpers.pm
+
+They work just like normal plugins and are also subclasses of
+L<Mojolicious::Plugin>.
+
+  package MyApp::Plugin::MyHelpers;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+    $app->helper(render_with_header => sub {
+      my ($c, @args) = @_;
+      $c->res->headers->header('X-Mojo' => 'I <3 Mojolicious!')
+      $c->render(@args);
+    });
+  }
+
+  1;
+
+You can have as many application specific plugins as you like, the only
+difference to normal plugins is that you load them using their full class
+name.
+
+  use Mojolicious::Lite;
+
+  use lib 'lib';
+
+  plugin 'MyApp::Plugin::MyHelpers';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->render_with_header(text => 'I ♥ Mojolicious!');
+  };
+
+  app->start;
+
 =head2 Adding commands to Mojolicious
 
 By now you've probably used many of the built-in commands described in
@@ -34,8 +34,9 @@ without compromises. While there are no rules in
 L<Mojolicious::Guides::Contributing> that forbid dependencies, we do currently
 discourage adding non-optional ones in favor of a faster and more painless
 installation process. And we do in fact already use several optional CPAN
-modules such as L<EV>, L<IO::Socket::IP>, L<IO::Socket::SSL> and L<Plack> to
-provide advanced functionality if they are installed.
+modules such as L<EV>, L<IO::Socket::IP>, L<IO::Socket::Socks>,
+L<IO::Socket::SSL> and L<Plack> to provide advanced functionality if they are
+installed.
 
 =head2 Why reinvent wheels?
 
@@ -149,7 +149,8 @@ organized CPAN distribution to maximize maintainability.
   |- lib                     # Library directory
   |  |- MyApp.pm             # Application class
   |  +- MyApp                # Application namespace
-  |     +- Example.pm        # Controller class
+  |     +- Controller        # Controller namespace
+  |        +- Example.pm     # Controller class
   |- t                       # Test directory
   |  +- basic.t              # Random test
   |- log                     # Log directory
@@ -205,14 +206,16 @@ In L<Mojolicious> we consider web applications simple frontends for existing
 business logic, that means L<Mojolicious> is by design entirely L<model> layer
 agnostic and you just use whatever Perl modules you like most.
 
-  $ mkdir lib
-  $ touch lib/MyUsers.pm
-  $ chmod 644 lib/MyUsers.pm
+  $ mkdir -p lib/MyApp/Model
+  $ touch lib/MyApp/Model/Users.pm
+  $ chmod 644 lib/MyApp/Model/Users.pm
 
 Our login manager will simply use a plain old Perl module abstracting away all
-logic related to matching usernames and passwords.
+logic related to matching usernames and passwords. The name
+C<MyApp::Model::Users> is an arbitrary choice, and is simply used to make the
+separation of concerns more visible.
 
-  package MyUsers;
+  package MyApp::Model::Users;
 
   use strict;
   use warnings;
@@ -245,10 +248,10 @@ templates.
   use Mojolicious::Lite;
 
   use lib 'lib';
-  use MyUsers;
+  use MyApp::Model::Users;
 
   # Helper to lazy initialize and store our model object
-  helper users => sub { state $users = MyUsers->new };
+  helper users => sub { state $users = MyApp::Model::Users->new };
 
   # /?user=sri&pass=secr3t
   any '/' => sub {
@@ -393,12 +396,12 @@ this.
   use Mojolicious::Lite;
 
   use lib 'lib';
-  use MyUsers;
+  use MyApp::Model::Users;
 
   # Make signed cookies secure
   app->secrets(['Mojolicious rocks']);
 
-  helper users => sub { state $users = MyUsers->new };
+  helper users => sub { state $users = MyApp::Model::Users->new };
 
   # Main login action
   any '/' => sub {
@@ -514,13 +517,13 @@ actual action code needs to be changed.
   package MyApp;
   use Mojo::Base 'Mojolicious';
 
-  use MyUsers;
+  use MyApp::Model::Users;
 
   sub startup {
     my $self = shift;
 
     $self->secrets(['Mojolicious rocks']);
-    $self->helper(users => sub { state $users = MyUsers->new });
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
 
     my $r = $self->routes;
 
@@ -578,14 +581,14 @@ allow running tests again.
 Hybrid routes are a nice intermediate step, but to maximize maintainability it
 makes sense to split our action code from its routing information.
 
-  $ mkdir lib/MyApp
-  $ touch lib/MyApp/Login.pm
-  $ chmod 644 lib/MyApp/Login.pm
+  $ mkdir lib/MyApp/Controller
+  $ touch lib/MyApp/Controller/Login.pm
+  $ chmod 644 lib/MyApp/Controller/Login.pm
 
 Once again the actual action code does not need to change, we just rename
 C<$c> to C<$self> since the controller is now the invocant.
 
-  package MyApp::Login;
+  package MyApp::Controller::Login;
   use Mojo::Base 'Mojolicious::Controller';
 
   sub index {
@@ -626,18 +629,20 @@ information.
   package MyApp;
   use Mojo::Base 'Mojolicious';
 
-  use MyUsers;
+  use MyApp::Model::Users;
 
   sub startup {
     my $self = shift;
 
     $self->secrets(['Mojolicious rocks']);
-    $self->helper(users => sub { state $users = MyUsers->new });
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
 
     my $r = $self->routes;
     $r->any('/')->to('login#index')->name('index');
-    my $logged_in = $r->under->to('login#logged_in');
+
+    my $logged_in = $r->under('/')->to('login#logged_in');
     $logged_in->get('/protected')->to('login#protected');
+
     $r->get('/logout')->to('login#logout');
   }
 
@@ -648,8 +653,8 @@ most.
 
 =head2 Templates
 
-Templates are usually bound to controllers, so they need to be moved into the
-appropriate directories.
+Templates are our views, and usually bound to controllers, so they need to be
+moved into the appropriate directories.
 
   $ mkdir templates/login
   $ mv templates/index.html.ep templates/login/index.html.ep
@@ -24,12 +24,19 @@ utilizing multiple template systems and data encoding modules.
 
 Templates can be automatically detected if enough information is provided by
 the developer or routes. Template names are expected to follow the
-C<name.format.handler> scheme, with C<name> defaulting to C<controller/action>
-or the route name, C<format> defaulting to C<html> and C<handler> to C<ep>.
+C<template.format.handler> scheme, with C<template> defaulting to
+C<controller/action> or the route name, C<format> defaulting to C<html> and
+C<handler> to C<ep>.
 
   {controller => 'users', action => 'list'} -> 'users/list.html.ep'
-  {name => 'foo', format => 'txt'}          -> 'foo.txt.ep'
-  {name => 'foo', handler => 'epl'}         -> 'foo.html.epl'
+  {template => 'foo', format => 'txt'}      -> 'foo.txt.ep'
+  {template => 'foo', handler => 'epl'}     -> 'foo.html.epl'
+
+The C<controller> value gets decamelized using L<Mojo::Util/"decamelize"> and
+C<-> characters replaced with C</>.
+
+  {controller => 'My::Users', action => 'add'} -> 'my/users/add.html.ep'
+  {controller => 'my-users', action => 'show'} -> 'my/users/show.html.ep'
 
 All templates should be in the C<templates> directories of the application,
 which can be customized with L<Mojolicious::Renderer/"paths">, or one of the
@@ -145,6 +152,11 @@ controller object as both C<$self> and C<$c>.
 
   Hello <%= $name %> from <%= $c->tx->remote_address %>.
 
+A prefix like C<myapp.*> is commonly used for stash values that you don't want
+to expose in templates.
+
+  $c->stash('myapp.name' => 'tester');
+
 There are also many helper functions available, but more about that later.
 
   <%= dumper {foo => 'bar'} %>
@@ -295,16 +307,16 @@ These mappings can be easily extended or changed with L<Mojolicious/"types">.
 
 =head2 Stash data
 
-Any of the native Perl data types can be passed to templates through the
-L<Mojolicious::Controller/"stash">.
+Any of the native Perl data types can be passed to templates as references
+through the L<Mojolicious::Controller/"stash">.
 
   $c->stash(author     => 'Sebastian');
   $c->stash(frameworks => [qw(Catalyst Mojolicious)]);
-  $c->stash(examples   => {tweetylicious => 'a microblogging app'});
+  $c->stash(examples   => {convos => 'an IRC app'});
 
   %= $author
   %= $frameworks->[1]
-  %= $examples->{tweetylicious}
+  %= $examples->{convos}
 
 Since everything is just Perl normal control structures just work.
 
@@ -312,10 +324,51 @@ Since everything is just Perl normal control structures just work.
     <%= $framework %> was written by <%= $author %>.
   % }
 
-  % if (my $description = $examples->{tweetylicious}) {
-    Tweetylicious is a <%= $description %>.
+  % if (my $description = $examples->{convos}) {
+    Convos is a <%= $description %>.
   % }
 
+For templates that might get rendered in different ways and where you're not
+sure if a stash value will actually be set, you can just use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"stash">.
+
+  % if (my $examples = stash 'examples') {
+    Convos is <%= $examples->{convos} %>.
+  % }
+
+=head2 Helpers
+
+Helpers are little functions you can use in templates as well as application
+and controller code.
+
+  # Template
+  %= dumper [1, 2, 3]
+
+  # Application
+  my $serialized = $app->dumper([1, 2, 3]);
+
+  # Controller
+  my $serialized = $c->dumper([1, 2, 3]);
+
+The helper L<Mojolicious::Plugin::DefaultHelpers/"dumper"> for example will
+use L<Data::Dumper> to serialize whatever data structure you pass it, this can
+be very useful for debugging. We differentiate between default helpers which
+are more general purpose like C<dumper> and tag helpers, which are template
+specific and mostly used to generate HTML tags.
+
+  %= link_to 'http://mojolicio.us' => begin
+    Mojolicious
+  % end
+
+In controllers you can also use the method
+L<Mojolicious::Controller/"helpers"> to fully qualify helper calls and ensure
+that they don't conflict with existing methods you may already have.
+
+  my $serialized = $c->helpers->dumper([1, 2, 3]);
+
+A list of all built-in helpers can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
 =head2 Content negotiation
 
 For resources with different representations and that require truly
@@ -455,29 +508,6 @@ passed to the renderer.
 
   app->start;
 
-=head2 Helpers
-
-Helpers are little functions you can use in templates and controller code.
-
-  %= dumper [1, 2, 3]
-
-  my $serialized = $c->dumper([1, 2, 3]);
-
-The helper L<Mojolicious::Plugin::DefaultHelpers/"dumper"> for example will
-use L<Data::Dumper> to serialize whatever data structure you pass it, this can
-be very useful for debugging. We differentiate between default helpers which
-are more general purpose like C<dumper> and tag helpers, which are template
-specific and mostly used to generate HTML tags.
-
-  %= javascript '/script.js'
-
-  %= javascript begin
-    var a = 'b';
-  % end
-
-A list of all built-in helpers can be found in
-L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
-
 =head2 Layouts
 
 Most of the time when using C<ep> templates you will want to wrap your
@@ -669,12 +699,12 @@ This chain could go on and on to allow a very high level of template reuse.
 
 =head2 Form validation
 
-You can use L<Mojolicious::Controller/"validation"> to validate C<GET>/C<POST>
-parameters submitted to your application. All unknown fields will be ignored
-by default, so you have to decide which should be required or optional before
-you can perform checks on their values. Every check is performed right away,
-so you can use the results immediately to build more advanced validation logic
-with methods like L<Mojolicious::Validator::Validation/"is_valid">.
+You can use L<Mojolicious::Controller/"validation"> to validate C<GET> and
+C<POST> parameters submitted to your application. All unknown fields will be
+ignored by default, so you have to decide which should be required or optional
+before you can perform checks on their values. Every check is performed right
+away, so you can use the results immediately to build more advanced validation
+logic with methods like L<Mojolicious::Validator::Validation/"is_valid">.
 
   use Mojolicious::Lite;
 
@@ -842,7 +872,7 @@ The token can also be submitted with the C<X-CSRF-Token> request header.
 =head2 Adding helpers
 
 Adding and redefining helpers is very easy, you can use them to do pretty much
-everything.
+anything.
 
   use Mojolicious::Lite;
 
@@ -863,7 +893,9 @@ everything.
   % debug 'Hello from a template!';
 
 Helpers can also accept template blocks as last argument, this for example
-allows very pleasant to use tag helpers and filters.
+allows very pleasant to use tag helpers and filters. Wrapping the helper
+result into a L<Mojo::ByteStream> object can prevent accidental double
+escaping.
 
   use Mojolicious::Lite;
   use Mojo::ByteStream;
@@ -887,8 +919,37 @@ allows very pleasant to use tag helpers and filters.
     More text.
   % end
 
-Wrapping the helper result into a L<Mojo::ByteStream> object can prevent
-accidental double escaping.
+Similar to stash values you can use a prefix like C<myapp.*> to keep helpers
+from getting exposed in templates and to organize them into namespaces as your
+application grows. Every prefix automatically becomes a helper that returns a
+proxy object containing the current controller object and on which you can
+call the nested helpers.
+
+  use Mojolicious::Lite;
+
+  helper 'cache_control.no_caching' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('private, max-age=0, no-cache');
+  };
+
+  helper 'cache_control.five_minutes' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('public, max-age=300');
+  };
+
+  get '/news' => sub {
+    my $c = shift;
+    $c->cache_control->no_caching;
+    $c->render(text => 'Always up to date.');
+  };
+
+  get '/some_older_story' => sub {
+    my $c = shift;
+    $c->cache_control->five_minutes;
+    $c->render(text => 'This one can be cached for a bit.');
+  };
+
+  app->start;
 
 =head2 Helper plugins
 
@@ -25,10 +25,10 @@ This black box is usually called a dispatcher. There are many implementations
 using different strategies to establish these connections, but pretty much all
 are based around mapping the requests path to some kind of response generator.
 
-  /user/show/1 -> $c->render(text => 'Sebastian');
-  /user/show/2 -> $c->render(text => 'Sara');
-  /user/show/3 -> $c->render(text => 'Baerbel');
-  /user/show/4 -> $c->render(text => 'Wolfgang');
+  /user/show/2 -> $c->render(text => 'Daniel');
+  /user/show/3 -> $c->render(text => 'Sara');
+  /user/show/4 -> $c->render(text => 'Baerbel');
+  /user/show/5 -> $c->render(text => 'Wolfgang');
 
 While it is very well possible to make all these connections static, it is
 also rather inefficient. That's why regular expressions are commonly used to
@@ -169,11 +169,11 @@ defined.
 
   1;
 
-The minimal route above will load and instantiate the class C<MyApp::Foo> and
-call its C<welcome> method.
+The minimal route above will load and instantiate the class
+C<MyApp::Controller::Foo> and call its C<welcome> method.
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -259,9 +259,10 @@ old ones.
 When the dispatcher sees C<controller> and C<action> values in the stash it
 will always try to turn them into a class and method to dispatch to. The
 C<controller> value gets camelized using L<Mojo::Util/"camelize"> and
-prefixed with a C<namespace> (defaulting to the applications class). While the
-action value is not changed at all, because of this both values are case
-sensitive.
+appended to one or more namespaces, defaulting to a controller namespace based
+on the application class (C<MyApp::Controller>), as well as the bare
+application class (C<MyApp>), and these namespaces are searched in that order.
+The action value is not changed at all, so both values are case sensitive.
 
   # Application
   package MyApp;
@@ -270,14 +271,14 @@ sensitive.
   sub startup {
     my $self = shift;
 
-    # /bye -> {controller => 'foo', action => 'bye'} -> MyApp::Foo->bye
+    # /bye -> MyApp::Controller::Foo->bye
     $self->routes->route('/bye')->to(controller => 'foo', action => 'bye');
   }
 
   1;
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -298,12 +299,18 @@ C<controller#action>.
   # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
   $r->route('/bye')->to('foo#bye', mymessage => 'Bye');
 
-During camelization C<-> gets replaced with C<::>, this allows multi-level
-C<controller> hierarchies.
+During camelization C<-> characters get replaced with C<::>, this allows
+multi-level C<controller> hierarchies.
 
-  # / -> {controller => 'foo-bar', action => 'hi'} -> MyApp::Foo::Bar->hi
+  # / -> MyApp::Controller::Foo::Bar->hi
   $r->route('/')->to('foo-bar#hi');
 
+You can also just specify the C<controller> in CamelCase form instead of
+snake_case.
+
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->route('/')->to('Foo::Bar#hi');
+
 For security reasons the dispatcher will always check if the C<controller> is
 actually a subclass of L<Mojolicious::Controller> or L<Mojo> before
 dispatching to it.
@@ -313,22 +320,22 @@ dispatching to it.
 You can use the C<namespace> stash value to change the namespace of a whole
 route with all its children.
 
-  # /bye -> MyApp::Controller::Foo::Bar->bye
+  # /bye -> MyApp::MyController::Foo::Bar->bye
   $r->route('/bye')
-    ->to(namespace => 'MyApp::Controller::Foo::Bar', action => 'bye');
+    ->to(namespace => 'MyApp::MyController::Foo::Bar', action => 'bye');
 
-The C<controller> is always appended to the C<namespace> if available.
+The C<controller> is always appended to this C<namespace> if available.
 
-  # /bye -> MyApp::Controller::Foo::Bar->bye
-  $r->route('/bye')->to('foo-bar#bye', namespace => 'MyApp::Controller');
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->route('/bye')->to('foo-bar#bye', namespace => 'MyApp::MyController');
 
-  # /hey -> MyApp::Controller::Foo::Bar->hey
-  $r->route('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::Controller');
+  # /hey -> MyApp::MyController::Foo::Bar->hey
+  $r->route('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::MyController');
 
 You can also change the default namespaces for all routes in the application
 with the router attribute L<Mojolicious::Routes/"namespaces">.
 
-  $r->namespaces(['MyApp::Controller']);
+  $r->namespaces(['MyApp::MyController']);
 
 =head2 Route to callback
 
@@ -429,13 +436,8 @@ automatically detected and stored in the stash value C<format>.
   $r->route('/foo')->to(controller => 'foo', action => 'bar');
 
 This for example allows multiple templates in different formats to share the
-same code.
-
-  # /foo      -> {controller => 'foo', action => 'bar'}
-  # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
-  $r->route('/foo')->to(controller => 'foo', action => 'bar');
-
-Restrictive placeholders can also be used.
+same action code. Restrictive placeholders can also be used to limit the
+allowed formats.
 
   # /foo.rss -> {controller => 'foo', action => 'bar', format => 'rss'}
   # /foo.xml -> {controller => 'foo', action => 'bar', format => 'xml'}
@@ -538,7 +540,7 @@ additional information.
   $r->websocket('/echo')->to(controller => 'foo', action => 'echo');
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -663,6 +665,19 @@ L<Mojolicious::Routes::Route/"under">.
     $c->render(text => 'Go away.');
   });
 
+=head2 Catch-all route
+
+Since routes match in the order in which they were defined, you can catch all
+requests that did not match in your last route with an optional wildcard
+placeholder.
+
+  # * /*
+  $r->any('/*whatever' => {whatever => ''} => sub {
+    my $c        = shift;
+    my $whatever = $c->param('whatever');
+    $c->render(text => "/$whatever did not match.", status => 404);
+  });
+
 =head2 Hooks
 
 Hooks operate outside the routing system and allow you to extend
@@ -785,10 +800,10 @@ to list all available routes together with name and underlying regular
 expressions.
 
   $ ./myapp.pl routes -v
-  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+))(?:\.([^/]+)$)?
+  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+)  ^/?(?:\.([^/]+))?$
   /bar        B...  *     bar      ^/bar
-    +/baz     ...W  GET   baz      ^/baz(?:\.([^/]+)$)?
-  /yada       ....  *     yada     ^/yada(?:\.([^/]+)$)?
+    +/baz     ...W  GET   baz      ^/baz            ^/?(?:\.([^/]+))?$
+  /yada       ....  *     yada     ^/yada           ^/?(?:\.([^/]+))?$
 
 =head1 ADVANCED
 
@@ -904,7 +919,7 @@ controller. This allows for example the use of the L<Mojolicious::Lite> domain
 specific language in normal L<Mojolicious> controllers.
 
   # Controller
-  package MyApp::Bar;
+  package MyApp::Controller::Bar;
   use Mojolicious::Lite;
 
   # /hello
@@ -927,7 +942,7 @@ path will be passed along in the C<path> stash value.
 A minimal embeddable application is nothing more than a subclass of L<Mojo>,
 containing a C<handler> method accepting L<Mojolicious::Controller> objects.
 
-  package MyApp::Bar;
+  package MyApp::Controller::Bar;
   use Mojo::Base 'Mojo';
 
   sub handler {
@@ -725,7 +725,7 @@ served automatically from a C<public> directory if it exists.
   $ mv something.js public/something.js
   $ mv mojolicious.tar.gz public/mojolicious.tar.gz
 
-Both have a higher precedence than routes.
+Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
 
 =head2 External templates
 
@@ -29,7 +29,7 @@ sub register {
 
   $app->helper($_ => $self->can("_$_"))
     for qw(accepts content content_for csrf_token current_route delay),
-    qw(inactivity_timeout url_with);
+    qw(inactivity_timeout is_fresh url_with);
   $app->helper(b => sub { shift; Mojo::ByteStream->new(@_) });
   $app->helper(c => sub { shift; Mojo::Collection->new(@_) });
   $app->helper(config  => sub { shift->app->config(@_) });
@@ -76,10 +76,10 @@ sub _current_route {
 }
 
 sub _delay {
-  my $self  = shift;
-  my $tx    = $self->render_later->tx;
+  my $c     = shift;
+  my $tx    = $c->render_later->tx;
   my $delay = Mojo::IOLoop->delay(@_);
-  $delay->catch(sub { $self->render_exception(pop) and undef $tx })->wait;
+  $delay->catch(sub { $c->render_exception(pop) and undef $tx })->wait;
 }
 
 sub _inactivity_timeout {
@@ -87,6 +87,11 @@ sub _inactivity_timeout {
   $stream->timeout(shift);
 }
 
+sub _is_fresh {
+  my ($c, %options) = @_;
+  return $c->app->static->is_fresh($c, \%options);
+}
+
 sub _url_with {
   my $c = shift;
   return $c->url_for(@_)->query($c->req->url->query->clone);
@@ -270,6 +275,21 @@ timeout if possible.
 
 Alias for C<Mojolicious::Controller/"render_to_string">.
 
+=head2 is_fresh
+
+  my $bool = $c->is_fresh;
+  my $bool = $c->is_fresh(etag => 'abc');
+  my $bool = $c->is_fresh(last_modified => $epoch);
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers with L<Mojolicious::Static/"is_fresh">.
+
+  # Add ETag header and check freshness before rendering
+  $c->is_fresh(etag => 'abc')
+    ? $c->rendered(304)
+    : $c->render(text => 'I ♥ Mojolicious!');
+
 =head2 layout
 
   % layout 'green';
@@ -9,17 +9,8 @@ sub register { $_[1]->renderer->add_handler(epl => \&_epl) }
 sub _epl {
   my ($renderer, $c, $output, $options) = @_;
 
-  # Template
-  my $name   = $renderer->template_name($options);
-  my $inline = $options->{inline};
-  $name = md5_sum encode('UTF-8', $inline) if defined $inline;
-  return undef unless defined $name;
-
   # Cached
-  my $key   = delete $options->{cache} || $name;
-  my $cache = $renderer->cache;
-  my $mt    = $cache->get($key);
-  $mt ||= $cache->set($key => Mojo::Template->new)->get($key);
+  my $mt = delete $options->{'mojo.template'} || Mojo::Template->new;
   my $log = $c->app->log;
   if ($mt->compiled) {
     $log->debug("Rendering cached @{[$mt->name]}.");
@@ -28,6 +19,9 @@ sub _epl {
 
   # Not cached
   else {
+    my $inline = $options->{inline};
+    my $name = defined $inline ? md5_sum encode('UTF-8', $inline) : undef;
+    return undef unless defined($name //= $renderer->template_name($options));
 
     # Inline
     if (defined $inline) {
@@ -37,7 +31,7 @@ sub _epl {
 
     # File
     else {
-      $mt->encoding($renderer->encoding) if $renderer->encoding;
+      if (my $encoding = $renderer->encoding) { $mt->encoding($encoding) }
 
       # Try template
       if (defined(my $path = $renderer->template_path($options))) {
@@ -79,8 +73,8 @@ Mojolicious::Plugin::EPLRenderer - Embedded Perl Lite renderer plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::EPLRenderer> is a renderer for C<epl> templates. C<epl>
-templates are pretty much just raw L<Mojo::Template>.
+L<Mojolicious::Plugin::EPLRenderer> is a renderer for C<epl> templates, which
+are pretty much just raw L<Mojo::Template>.
 
 This is a core plugin, that means it is always enabled and its code a good
 example for learning to build new plugins, you're welcome to fork it.
@@ -4,37 +4,38 @@ use Mojo::Base 'Mojolicious::Plugin';
 use Mojo::Template;
 use Mojo::Util qw(encode md5_sum monkey_patch);
 
+sub DESTROY { Mojo::Util::_teardown(shift->{namespace}) }
+
 sub register {
   my ($self, $app, $conf) = @_;
 
   # Auto escape by default to prevent XSS attacks
   my $template = {auto_escape => 1, %{$conf->{template} || {}}};
+  my $ns = $self->{namespace} = $template->{namespace}
+    //= 'Mojo::Template::Sandbox::' . md5_sum "$self";
 
   # Add "ep" handler and make it the default
   $app->renderer->default_handler('ep')->add_handler(
     $conf->{name} || 'ep' => sub {
       my ($renderer, $c, $output, $options) = @_;
 
-      # Generate name
-      my $name = $options->{inline} || $renderer->template_name($options);
+      my $name = $options->{inline} // $renderer->template_name($options);
       return undef unless defined $name;
       my @keys = sort grep {/^\w+$/} keys %{$c->stash};
-      my $id = encode 'UTF-8', join(',', $name, @keys);
-      my $key = $options->{cache} = md5_sum $id;
+      my $key = md5_sum encode 'UTF-8', join(',', $name, @keys);
 
-      # Cache template for "epl" handler
+      # Prepare template for "epl" handler
       my $cache = $renderer->cache;
-      my $mt    = $cache->get($key);
-      unless ($mt) {
-        $mt = Mojo::Template->new($template);
+      unless ($options->{'mojo.template'} = $cache->get($key)) {
+        my $mt = $options->{'mojo.template'} = Mojo::Template->new($template);
 
         # Helpers (only once)
-        ++$self->{helpers} and _helpers($mt->namespace, $renderer->helpers)
+        ++$self->{helpers} and _helpers($ns, $renderer->helpers)
           unless $self->{helpers};
 
         # Stash values (every time)
         my $prepend = 'my $self = my $c = shift; my $_S = $c->stash;';
-        $prepend .= " my \$$_ = \$_S->{'$_'};" for @keys;
+        $prepend .= join '', map {" my \$$_ = \$_S->{'$_'};"} @keys;
 
         $cache->set($key => $mt->prepend($prepend . $mt->prepend));
       }
@@ -42,7 +43,7 @@ sub register {
       # Make current controller available
       no strict 'refs';
       no warnings 'redefine';
-      local *{"@{[$mt->namespace]}::_C"} = sub {$c};
+      local *{"${ns}::_C"} = sub {$c};
 
       # Render with "epl" handler
       return $renderer->handlers->{epl}->($renderer, $c, $output, $options);
@@ -51,10 +52,10 @@ sub register {
 }
 
 sub _helpers {
-  my ($ns, $helpers) = @_;
-  for my $name (grep {/^\w+$/} keys %$helpers) {
-    monkey_patch $ns, $name,
-      sub { $ns->_C->app->renderer->helpers->{$name}->($ns->_C, @_) };
+  my ($class, $helpers) = @_;
+  for my $method (grep {/^\w+$/} keys %$helpers) {
+    my $sub = $helpers->{$method};
+    monkey_patch $class, $method, sub { $class->_C->$sub(@_) };
   }
 }
 
@@ -80,13 +81,14 @@ Mojolicious::Plugin::EPRenderer - Embedded Perl renderer plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::EPRenderer> is a renderer for C<ep> templates.
+L<Mojolicious::Plugin::EPRenderer> is a renderer for C<ep> or C<Embedded Perl>
+templates.
 
-C<ep> or C<Embedded Perl> is a simple template format where you embed perl
-code into documents. It is based on L<Mojo::Template>, but extends it with
-some convenient syntax sugar designed specifically for L<Mojolicious>. It
-supports L<Mojolicious> template helpers and exposes the stash directly as
-Perl variables.
+C<Embedded Perl> is a simple template format where you embed perl code into
+documents. It is based on L<Mojo::Template>, but extends it with some
+convenient syntax sugar designed specifically for L<Mojolicious>. It supports
+L<Mojolicious> template helpers and exposes the stash directly as Perl
+variables.
 
 This is a core plugin, that means it is always enabled and its code a good
 example for learning to build new plugins, you're welcome to fork it.
@@ -6,7 +6,7 @@ use Mojo::ByteStream 'b';
 use Mojo::DOM;
 use Mojo::URL;
 use Mojo::Util qw(slurp unindent url_escape);
-use Pod::Simple::HTML;
+use Pod::Simple::XHTML 3.09;
 use Pod::Simple::Search;
 
 sub register {
@@ -35,19 +35,17 @@ sub register {
 }
 
 sub _html {
-  my ($self, $src) = @_;
+  my ($c, $src) = @_;
 
   # Rewrite links
   my $dom     = Mojo::DOM->new(_pod_to_html($src));
-  my $perldoc = $self->url_for('/perldoc/');
-  for my $e ($dom->find('a[href]')->each) {
-    my $attrs = $e->attr;
-    $attrs->{href} =~ s!%3A%3A!/!gi
-      if $attrs->{href} =~ s!^http://search\.cpan\.org/perldoc\?!$perldoc!;
-  }
+  my $perldoc = $c->url_for('/perldoc/');
+  $_->{href} =~ s!^http://metacpan\.org/pod/!$perldoc!
+    and $_->{href} =~ s!::!/!gi
+    for $dom->find('a[href]')->attr->each;
 
   # Rewrite code blocks for syntax highlighting and correct indentation
-  for my $e ($dom->find('pre')->each) {
+  for my $e ($dom->find('pre > code')->each) {
     $e->content(my $str = unindent $e->content);
     next if $str =~ /^\s*(?:\$|Usage:)\s+/m || $str !~ /[\$\@\%]\w|-&gt;\w/m;
     my $attrs = $e->attr;
@@ -57,23 +55,15 @@ sub _html {
 
   # Rewrite headers
   my $toc = Mojo::URL->new->fragment('toc');
-  my (%anchors, @parts);
+  my @parts;
   for my $e ($dom->find('h1, h2, h3')->each) {
 
-    # Anchor and text
-    my $name = my $text = $e->all_text;
-    $name =~ s/\s+/_/g;
-    $name =~ s/[^\w\-]//g;
-    my $anchor = $name;
-    my $i      = 1;
-    $anchor = $name . $i++ while $anchors{$anchor}++;
-
-    # Rewrite
     push @parts, [] if $e->type eq 'h1' || !@parts;
-    my $link = Mojo::URL->new->fragment($anchor);
-    push @{$parts[-1]}, $text, $link;
-    my $permalink = $self->link_to('#' => $link, class => 'permalink');
-    $e->content($permalink . $self->link_to($text => $toc, id => $anchor));
+    my $anchor = $e->{id};
+    my $link   = Mojo::URL->new->fragment($anchor);
+    push @{$parts[-1]}, my $text = $e->all_text, $link;
+    my $permalink = $c->link_to('#' => $link, class => 'permalink');
+    $e->content($permalink . $c->link_to($text => $toc, id => $anchor));
   }
 
   # Try to find a title
@@ -81,39 +71,34 @@ sub _html {
   $dom->find('h1 + p')->first(sub { $title = shift->text });
 
   # Combine everything to a proper response
-  $self->content_for(perldoc => "$dom");
-  my $template = $self->app->renderer->_bundled('perldoc');
-  $self->render(inline => $template, title => $title, parts => \@parts);
+  $c->content_for(perldoc => "$dom");
+  my $template = $c->app->renderer->_bundled('perldoc');
+  $c->render(inline => $template, title => $title, parts => \@parts);
 }
 
 sub _perldoc {
-  my $self = shift;
+  my $c = shift;
 
   # Find module or redirect to CPAN
-  my $module = $self->param('module');
-  $module =~ s!/!::!g;
+  my $module = join '::', split '/', scalar $c->param('module');
   my $path
     = Pod::Simple::Search->new->find($module, map { $_, "$_/pods" } @INC);
-  return $self->redirect_to("http://metacpan.org/module/$module")
+  return $c->redirect_to("http://metacpan.org/pod/$module")
     unless $path && -r $path;
 
   my $src = slurp $path;
-  $self->respond_to(txt => {data => $src}, html => sub { _html($self, $src) });
+  $c->respond_to(txt => {data => $src}, html => sub { _html($c, $src) });
 }
 
 sub _pod_to_html {
   return '' unless defined(my $pod = ref $_[0] eq 'CODE' ? shift->() : shift);
 
-  my $parser = Pod::Simple::HTML->new;
-  $parser->$_('') for qw(force_title html_header_before_title);
-  $parser->$_('') for qw(html_header_after_title html_footer);
+  my $parser = Pod::Simple::XHTML->new;
+  $parser->perldoc_url_prefix('http://metacpan.org/pod/');
+  $parser->$_('') for qw(html_header html_footer);
   $parser->output_string(\(my $output));
   return $@ unless eval { $parser->parse_string_document("$pod"); 1 };
 
-  # Filter
-  $output =~ s!<a name='___top' class='dummyTopAnchor'\s*?></a>\n!!g;
-  $output =~ s!<a class='u'.*?name=".*?"\s*>(.*?)</a>!$1!sg;
-
   return $output;
 }
 
@@ -45,7 +45,7 @@ sub register {
 
 sub _csrf_field {
   my $c = shift;
-  return _hidden_field($c, csrf_token => $c->csrf_token, @_);
+  return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_);
 }
 
 sub _form_for {
@@ -246,7 +246,7 @@ sub _text_area {
 sub _validation {
   my ($c, $name) = (shift, shift);
   return _tag(@_) unless $c->validation->has_error($name);
-  return $c->tag_with_error(@_);
+  return $c->helpers->tag_with_error(@_);
 }
 
 1;
@@ -7,7 +7,7 @@ has namespaces => sub { ['Mojolicious::Plugin'] };
 
 sub emit_hook {
   my $self = shift;
-  $_->(@_) for @{$self->subscribers(shift)};
+  for my $cb (@{$self->subscribers(shift)}) { $cb->(@_) }
   return $self;
 }
 
@@ -25,7 +25,7 @@ sub emit_chain {
 
 sub emit_hook_reverse {
   my $self = shift;
-  $_->(@_) for reverse @{$self->subscribers(shift)};
+  for my $cb (reverse @{$self->subscribers(shift)}) { $cb->(@_) }
   return $self;
 }
 
@@ -52,10 +52,9 @@ sub register_plugin {
 
 sub _load {
   my $module = shift;
-  if (my $e = Mojo::Loader->new->load($module)) {
-    ref $e ? die $e : return undef;
-  }
-  return $module->isa('Mojolicious::Plugin');
+  return $module->isa('Mojolicious::Plugin')
+    unless my $e = Mojo::Loader->new->load($module);
+  ref $e ? die $e : return undef;
 }
 
 1;
@@ -70,7 +69,7 @@ Mojolicious::Plugins - Plugin manager
 
   use Mojolicious::Plugins;
 
-  my $plugins = Mojolicious::Plugin->new;
+  my $plugins = Mojolicious::Plugins->new;
   push @{$plugins->namespaces}, 'MyApp::Plugin';
 
 =head1 DESCRIPTION
@@ -6,7 +6,7 @@ use Mojo::Cache;
 use Mojo::JSON 'encode_json';
 use Mojo::Home;
 use Mojo::Loader;
-use Mojo::Util qw(decamelize encode slurp);
+use Mojo::Util qw(decamelize encode md5_sum monkey_patch slurp);
 
 has cache   => sub { Mojo::Cache->new };
 has classes => sub { ['main'] };
@@ -29,6 +29,10 @@ $HOME->parse(
   $HOME->parse($HOME->mojo_lib_dir)->rel_dir('Mojolicious/templates'));
 my %TEMPLATES = map { $_ => slurp $HOME->rel_file($_) } @{$HOME->list_files};
 
+my $LOADER = Mojo::Loader->new;
+
+sub DESTROY { Mojo::Util::_teardown($_) for @{shift->{namespaces}} }
+
 sub accepts {
   my ($self, $c) = (shift, shift);
 
@@ -53,23 +57,31 @@ sub add_helper  { shift->_add(helpers  => @_) }
 sub get_data_template {
   my ($self, $options) = @_;
 
-  # Index DATA templates
-  my $loader = Mojo::Loader->new;
-  unless ($self->{index}) {
-    my $index = $self->{index} = {};
-    for my $class (reverse @{$self->classes}) {
-      $index->{$_} = $class for keys %{$loader->data($class)};
-    }
+  # Find template
+  return undef unless my $template = $self->template_name($options);
+  return $LOADER->data($self->{index}{$template}, $template);
+}
+
+sub get_helper {
+  my ($self, $name) = @_;
+
+  if (my $h = $self->{proxy}{$name} || $self->helpers->{$name}) { return $h }
+
+  my $found;
+  my $class = 'Mojolicious::Renderer::Helpers::' . md5_sum "$name:$self";
+  my $re = $name eq '' ? qr/^(([^.]+))/ : qr/^(\Q$name\E\.([^.]+))/;
+  for my $key (keys %{$self->helpers}) {
+    $key =~ $re ? ($found, my $method) = (1, $2) : next;
+    my $sub = $self->get_helper($1);
+    monkey_patch $class, $method => sub { ${shift()}->$sub(@_) };
   }
 
-  # Find template
-  my $template = $self->template_name($options);
-  return $loader->data($self->{index}{$template}, $template);
+  $found ? push @{$self->{namespaces}}, $class : return undef;
+  return $self->{proxy}{$name} = sub { bless \shift, $class };
 }
 
 sub render {
   my ($self, $c, $args) = @_;
-  $args ||= {};
 
   # Localize "extends" and "layout" to allow argument overrides
   my $stash = $c->stash;
@@ -81,7 +93,7 @@ sub render {
   delete @{$stash}{qw(layout extends)} if $ts;
 
   # Merge stash and arguments
-  %$stash = (%$stash, %$args);
+  @$stash{keys %$args} = values %$args;
 
   my $options = {
     encoding => $self->encoding,
@@ -153,7 +165,8 @@ sub template_for {
 sub template_handler {
   my ($self, $options) = @_;
   return undef unless my $file = $self->template_name($options);
-  return $self->default_handler unless my $handlers = $self->_handlers($file);
+  return $self->default_handler
+    unless my $handlers = $self->{templates}{$file};
   return $handlers->[0];
 }
 
@@ -164,11 +177,13 @@ sub template_name {
   return undef unless my $format   = $options->{format};
   $template .= ".$format";
 
+  $self->_warmup unless $self->{templates};
+
   # Variants
   my $handler = $options->{handler};
   if (defined(my $variant = $options->{variant})) {
     $variant = "$template+$variant";
-    my $handlers = $self->_handlers($variant) // [];
+    my $handlers = $self->{templates}{$variant} // [];
     $template = $variant
       if @$handlers && !defined $handler || grep { $_ eq $handler } @$handlers;
   }
@@ -194,6 +209,7 @@ sub template_path {
 sub _add {
   my ($self, $attr, $name, $cb) = @_;
   $self->$attr->{$name} = $cb;
+  delete $self->{proxy};
   return $self;
 }
 
@@ -206,24 +222,6 @@ sub _extends {
   return delete $stash->{extends};
 }
 
-sub _handlers {
-  my ($self, $file) = @_;
-
-  unless ($self->{templates}) {
-
-    # Templates
-    s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
-      for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};
-
-    # DATA templates
-    my $loader = Mojo::Loader->new;
-    s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
-      for map { sort keys %{$loader->data($_)} } @{$self->classes};
-  }
-
-  return $self->{templates}{$file};
-}
-
 sub _render_template {
   my ($self, $c, $output, $options) = @_;
 
@@ -239,6 +237,22 @@ sub _render_template {
   return undef;
 }
 
+sub _warmup {
+  my $self = shift;
+
+  my ($index, $templates) = @$self{qw(index templates)} = ({}, {});
+
+  # Handlers for templates
+  s/\.(\w+)$// and push @{$templates->{$_}}, $1
+    for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};
+
+  # Handlers and classes for DATA templates
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for my @keys = sort keys %{$LOADER->data($class)};
+    s/\.(\w+)$// and unshift @{$templates->{$_}}, $1 for reverse @keys;
+  }
+}
+
 1;
 
 =encoding utf8
@@ -252,7 +266,7 @@ Mojolicious::Renderer - Generate dynamic content
   use Mojolicious::Renderer;
 
   my $renderer = Mojolicious::Renderer->new;
-  push @{$renderer->classes}, 'MyApp::Foo';
+  push @{$renderer->classes}, 'MyApp::Controller::Foo';
   push @{$renderer->paths}, '/home/sri/templates';
 
 =head1 DESCRIPTION
@@ -372,9 +386,17 @@ Register a new helper.
 
 Get a C<DATA> section template by name, usually used by handlers.
 
+=head2 get_helper
+
+  my $helper = $renderer->get_helper('url_for');
+
+Get a helper by full name, generate a helper dynamically for a prefix or
+return C<undef> if no helper or prefix could be found. Generated helpers
+return a proxy object containing the current controller object and on which
+nested helpers can be called.
+
 =head2 render
 
-  my ($output, $format) = $renderer->render(Mojolicious::Controller->new);
   my ($output, $format) = $renderer->render(Mojolicious::Controller->new, {
     template => 'foo/bar',
     foo      => 'bar'
@@ -1,7 +1,7 @@
 package Mojolicious::Routes::Match;
 use Mojo::Base -base;
 
-use Mojolicious::Routes::Route;
+use Mojo::Util;
 
 has current => 0;
 has [qw(endpoint root)];
@@ -10,8 +10,7 @@ has stack => sub { [] };
 sub match { $_[0]->_match($_[0]->root, $_[1], $_[2]) }
 
 sub path_for {
-  my ($self, $name, %values)
-    = (shift, Mojolicious::Routes::Route::_values(@_));
+  my ($self, $name, %values) = (shift, Mojo::Util::_options(@_));
 
   # Current route
   my $endpoint;
@@ -40,11 +39,13 @@ sub _match {
   my ($self, $r, $c, $options) = @_;
 
   # Pattern
-  my $path = $options->{path};
-  return
-    unless my $captures = $r->pattern->match_partial(\$path, $r->is_endpoint);
+  my $path    = $options->{path};
+  my $partial = $r->partial;
+  my $detect  = (my $endpoint = $r->is_endpoint) && !$partial;
+  return unless my $captures = $r->pattern->match_partial(\$path, $detect);
   local $options->{path} = $path;
-  $captures = $self->{captures} = {%{$self->{captures} || {}}, %$captures};
+  local @{$self->{captures} ||= {}}{keys %$captures} = values %$captures;
+  $captures = $self->{captures};
 
   # Method
   my $methods = $r->via;
@@ -64,14 +65,13 @@ sub _match {
 
   # Partial
   my $empty = !length $path || $path eq '/';
-  if ($r->partial) {
+  if ($partial) {
     $captures->{path} = $path;
     $self->endpoint($r);
     $empty = 1;
   }
 
   # Endpoint (or bridge)
-  my $endpoint = $r->is_endpoint;
   if (($endpoint && $empty) || $r->inline) {
     push @{$self->stack}, {%$captures};
     if ($endpoint && $empty) {
@@ -20,12 +20,11 @@ sub match_partial {
   my ($self, $pathref, $detect) = @_;
 
   # Compile on demand
-  my $regex = $self->regex || $self->_compile;
-  my $format
-    = $detect ? ($self->format_regex || $self->_compile_format) : undef;
+  $self->_compile unless $self->{regex};
+  $self->_compile_format if $detect && !$self->{format_regex};
 
   # Match
-  return undef unless my @captures = $$pathref =~ $regex;
+  return undef unless my @captures = $$pathref =~ $self->regex;
   $$pathref = ${^POSTMATCH};
 
   # Merge captures
@@ -37,11 +36,10 @@ sub match_partial {
   }
 
   # Format
-  my $constraint = $self->constraints->{format};
-  return $captures if !$detect || defined $constraint && !$constraint;
-  if ($$pathref =~ s!^/?$format!!) { $captures->{format} = $1 }
-  elsif ($constraint) { return undef unless $captures->{format} }
-
+  return $captures unless $detect && (my $regex = $self->format_regex);
+  return undef unless $$pathref =~ $regex;
+  $captures->{format} = $1 if defined $1;
+  $$pathref = '';
   return $captures;
 }
 
@@ -61,29 +59,24 @@ sub parse {
 sub render {
   my ($self, $values, $render) = @_;
 
-  # Merge values with defaults
-  my $format = ($values ||= {})->{format};
-  my $defaults = $self->defaults;
-  $values = {%$defaults, %$values};
-
   # Placeholders can only be optional without a format
-  my $optional = !$format;
+  my $optional = !(my $format = $values->{format});
 
   my $str = '';
   for my $token (reverse @{$self->tree}) {
-    my ($op, $value) = @$token[0, 1];
+    my ($op, $value) = @$token;
     my $fragment = '';
 
-    # Slash
-    if ($op eq 'slash') { $fragment = '/' unless $optional }
-
     # Text
-    elsif ($op eq 'text') { ($fragment, $optional) = ($value, 0) }
+    if ($op eq 'text') { ($fragment, $optional) = ($value, 0) }
+
+    # Slash
+    elsif ($op eq 'slash') { $fragment = '/' unless $optional }
 
     # Placeholder
     else {
-      $fragment = $values->{$value} // '';
-      my $default = $defaults->{$value};
+      my $default = $self->defaults->{$value};
+      $fragment = $values->{$value} // $default // '';
       if (!defined $default || ($default ne $fragment)) { $optional = 0 }
       elsif ($optional) { $fragment = '' }
     }
@@ -106,35 +99,34 @@ sub _compile {
   my $block = my $regex = '';
   my $optional = 1;
   for my $token (reverse @{$self->tree}) {
-    my ($op, $value) = @$token[0, 1];
+    my ($op, $value) = @$token;
     my $fragment = '';
 
+    # Text
+    if ($op eq 'text') { ($fragment, $optional) = (quotemeta $value, 0) }
+
     # Slash
-    if ($op eq 'slash') {
+    elsif ($op eq 'slash') {
       $regex = ($optional ? "(?:/$block)?" : "/$block") . $regex;
       ($block, $optional) = ('', 1);
       next;
     }
 
-    # Text
-    elsif ($op eq 'text') { ($fragment, $optional) = (quotemeta $value, 0) }
-
     # Placeholder
-    elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') {
+    else {
       unshift @$placeholders, $value;
 
       # Placeholder
-      if ($op eq 'placeholder') { $fragment = '([^\/\.]+)' }
+      if ($op eq 'placeholder') { $fragment = '([^/\.]+)' }
 
       # Relaxed
-      elsif ($op eq 'relaxed') { $fragment = '([^\/]+)' }
+      elsif ($op eq 'relaxed') { $fragment = '([^/]+)' }
 
       # Wildcard
-      elsif ($op eq 'wildcard') { $fragment = '(.+)' }
+      else { $fragment = '(.+)' }
 
       # Custom regex
-      my $constraint = $constraints->{$value};
-      $fragment = _compile_req($constraint) if $constraint;
+      if (my $c = $constraints->{$value}) { $fragment = _compile_req($c) }
 
       # Optional placeholder
       exists $defaults->{$value} ? ($fragment .= '?') : ($optional = 0);
@@ -146,23 +138,23 @@ sub _compile {
   # Not rooted with a slash
   $regex = "$block$regex" if $block;
 
-  return $self->regex(qr/^$regex/ps)->regex;
+  $self->regex(qr/^$regex/ps);
 }
 
 sub _compile_format {
   my $self = shift;
 
   # Default regex
-  my $c = $self->constraints;
-  return $self->format_regex(qr!\.([^/]+)$!)->format_regex
-    unless defined $c->{format};
+  my $format = $self->constraints->{format};
+  return $self->format_regex(qr!^/?(?:\.([^/]+))?$!) unless defined $format;
 
   # No regex
-  return undef unless $c->{format};
+  return undef unless $format;
 
   # Compile custom regex
-  my $regex = _compile_req($c->{format});
-  return $self->format_regex(qr!\.$regex$!)->format_regex;
+  my $regex = '\.' . _compile_req($format);
+  $regex = "(?:$regex)?" if $self->defaults->{format};
+  $self->format_regex(qr!^/?$regex$!);
 }
 
 sub _compile_req {
@@ -204,18 +196,21 @@ sub _tokenize {
     # Quote end
     elsif ($char eq $quote_end) { ($inside, $quoted) = (0, 0) }
 
-    # Slash
+    # Slash (first slash is text for optimizations)
     elsif ($char eq '/') {
-      push @tree, ['slash'];
+      push @tree, @tree ? ['slash'] : ['text', '/'];
       $inside = 0;
     }
 
     # Placeholder, relaxed or wildcard
     elsif ($inside) { $tree[-1][-1] .= $char }
 
-    # Text
+    # Text (optimize text followed by slash followed by text)
     elsif ($tree[-1][0] eq 'text') { $tree[-1][-1] .= $char }
-    else                           { push @tree, ['text', $char] }
+    elsif ($tree[-2] && $tree[-2][0] eq 'text' && $tree[-1][0] eq 'slash') {
+      pop @tree && ($tree[-1][-1] .= "/$char");
+    }
+    else { push @tree, ['text', $char] }
   }
 
   return $self->pattern($pattern)->tree(\@tree);
@@ -271,8 +266,8 @@ Compiled regular expression for format matching.
 
 =head2 pattern
 
-  my $pattern = $pattern->pattern;
-  $pattern    = $pattern->pattern('/(foo)/(bar)');
+  my $raw  = $pattern->pattern;
+  $pattern = $pattern->pattern('/(foo)/(bar)');
 
 Raw unparsed pattern.
 
@@ -321,7 +316,7 @@ Character indicating a relaxed placeholder, defaults to C<#>.
 =head2 tree
 
   my $tree = $pattern->tree;
-  $pattern = $pattern->tree([['slash'], ['text', 'foo']]);
+  $pattern = $pattern->tree([['text', '/foo']]);
 
 Pattern in parsed form. Note that this structure should only be used very
 carefully since it is very dynamic.
@@ -2,6 +2,7 @@ package Mojolicious::Routes::Route;
 use Mojo::Base -base;
 
 use Carp 'croak';
+use Mojo::Util;
 use Mojolicious::Routes::Pattern;
 use Scalar::Util qw(blessed weaken);
 
@@ -12,7 +13,7 @@ has pattern    => sub { Mojolicious::Routes::Pattern->new };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
@@ -122,13 +123,15 @@ sub remove {
 sub render {
   my ($self, $path, $values) = @_;
 
-  # Render pattern
-  my $prefix = $self->pattern->render($values, !$path);
-  $path = "$prefix$path" unless $prefix eq '/';
-  $path ||= '/' unless my $parent = $self->parent;
+  # Render pattern (if necessary)
+  if (defined((my $pattern = $self->pattern)->pattern) || !$path) {
+    my $prefix = $pattern->render($values, !$path);
+    $path = "$prefix$path" unless $prefix eq '/';
+  }
 
   # Let parent render
-  return $parent ? $parent->render($path, $values) : $path;
+  return $path || '/' unless my $parent = $self->parent;
+  return $parent->render($path, $values);
 }
 
 sub root {
@@ -150,7 +153,7 @@ sub to {
 
   my $pattern = $self->pattern;
   return $pattern->defaults unless @_;
-  my ($shortcut, %defaults) = _values(@_);
+  my ($shortcut, %defaults) = Mojo::Util::_options(@_);
 
   if ($shortcut) {
 
@@ -166,7 +169,7 @@ sub to {
     }
   }
 
-  $pattern->defaults({%{$pattern->defaults}, %defaults});
+  @{$pattern->defaults}{keys %defaults} = values %defaults;
 
   return $self;
 }
@@ -229,18 +232,6 @@ sub _generate_route {
   return defined $name ? $route->name($name) : $route;
 }
 
-sub _values {
-
-  # Hash or name (one)
-  return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
-
-  # Name and values (odd)
-  return shift, @_ if @_ % 2;
-
-  # Name and hash or just values (even)
-  return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
-}
-
 1;
 
 =encoding utf8
@@ -608,11 +599,13 @@ variations.
 In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
 shortcuts provided by L</"root"> on L<Mojolicious::Routes::Route> objects.
 
+  # Add a "firefox" shortcut
   $r->root->add_shortcut(firefox => sub {
     my ($r, $path) = @_;
     $r->get($path, agent => qr/Firefox/);
   });
 
+  # Use "firefox" shortcut to generate routes
   $r->firefox('/welcome')->to('firefox#welcome');
   $r->firefox('/bye')->to('firefox#bye');
 
@@ -34,9 +34,8 @@ sub continue {
 
   # Merge captures into stash
   my $stash = $c->stash;
-  my $captures = $stash->{'mojo.captures'} //= {};
-  %$captures = (%$captures, %$field);
-  %$stash    = (%$stash,    %$field);
+  @{$stash->{'mojo.captures'} //= {}}{keys %$field} = values %$field;
+  @$stash{keys %$field} = values %$field;
 
   my $continue;
   my $last = !$stack->[++$current];
@@ -145,11 +144,8 @@ sub _class {
   for my $class (@classes) {
 
     # Failed
-    unless (my $found = $self->_load($class)) {
-      next unless defined $found;
-      $log->debug(qq{Class "$class" is not a controller.});
-      return undef;
-    }
+    next unless defined(my $found = $self->_load($class));
+    return !$log->debug(qq{Class "$class" is not a controller.}) unless $found;
 
     # Success
     my $new = $class->new(%$c);
@@ -289,7 +285,7 @@ C<attr>, C<has>, C<new> and C<tap>.
 Namespaces to load controllers from.
 
   # Add another namespace to load controllers from
-  push @{$r->namespaces}, 'MyApp::Controller';
+  push @{$r->namespaces}, 'MyApp::MyController';
 
 =head2 shortcuts
 
@@ -14,6 +14,7 @@ has serialize          => sub { \&Mojo::JSON::encode_json };
 sub load {
   my ($self, $c) = @_;
 
+  return unless $c->req->headers->cookie;
   return unless my $value = $c->signed_cookie($self->cookie_name);
   $value =~ y/-/=/;
   return unless my $session = $self->deserialize->(b64_decode $value);
@@ -7,6 +7,7 @@ use Mojo::Asset::Memory;
 use Mojo::Date;
 use Mojo::Home;
 use Mojo::Loader;
+use Mojo::Util 'md5_sum';
 
 has classes => sub { ['main'] };
 has paths   => sub { [] };
@@ -18,12 +19,19 @@ my $MTIME = time;
 my $HOME   = Mojo::Home->new;
 my $PUBLIC = $HOME->parse($HOME->mojo_lib_dir)->rel_dir('Mojolicious/public');
 
+my $LOADER = Mojo::Loader->new;
+
 sub dispatch {
   my ($self, $c) = @_;
 
+  # Method (GET or HEAD)
+  my $req    = $c->req;
+  my $method = $req->method;
+  return undef unless $method eq 'GET' || $method eq 'HEAD';
+
   # Canonical path
   my $stash = $c->stash;
-  my $path  = $c->req->url->path;
+  my $path  = $req->url->path;
   $path = $stash->{path} ? $path->new($stash->{path}) : $path->clone;
   return undef unless my @parts = @{$path->canonicalize->parts};
 
@@ -49,36 +57,55 @@ sub file {
   return $self->_get_file(catfile($PUBLIC, split('/', $rel)));
 }
 
+sub is_fresh {
+  my ($self, $c, $options) = @_;
+
+  my $res_headers = $c->res->headers;
+  my ($last, $etag) = @$options{qw(last_modified etag)};
+  $res_headers->last_modified(Mojo::Date->new($last)) if $last;
+  $res_headers->etag($etag = qq{"$etag"}) if $etag;
+
+  # Unconditional
+  my $req_headers = $c->req->headers;
+  my $match       = $req_headers->if_none_match;
+  return undef unless (my $since = $req_headers->if_modified_since) || $match;
+
+  # If-None-Match
+  return undef if $match && ($etag // $res_headers->etag // '') ne $match;
+
+  # If-Modified-Since
+  return !!$match unless ($last //= $res_headers->last_modified) && $since;
+  return _epoch($last) <= (_epoch($since) // 0);
+}
+
 sub serve {
   my ($self, $c, $rel) = @_;
+
   return undef unless my $asset = $self->file($rel);
+  my $headers = $c->res->headers;
+  return !!$self->serve_asset($c, $asset) if $headers->content_type;
+
+  # Content-Type
   my $types = $c->app->types;
   my $type = $rel =~ /\.(\w+)$/ ? $types->type($1) : undef;
-  $c->res->headers->content_type($type || $types->type('txt'));
+  $headers->content_type($type || $types->type('txt'));
   return !!$self->serve_asset($c, $asset);
 }
 
 sub serve_asset {
   my ($self, $c, $asset) = @_;
 
-  # Last modified
-  my $mtime = $asset->is_file ? (stat $asset->path)[9] : $MTIME;
+  # Last-Modified and ETag
   my $res = $c->res;
-  $res->code(200)->headers->last_modified(Mojo::Date->new($mtime))
-    ->accept_ranges('bytes');
-
-  # If modified since
-  my $headers = $c->req->headers;
-  if (my $date = $headers->if_modified_since) {
-    my $since = Mojo::Date->new($date)->epoch;
-    return $res->code(304) if defined $since && $since == $mtime;
-  }
+  $res->code(200)->headers->accept_ranges('bytes');
+  my $mtime = $asset->is_file ? (stat $asset->path)[9] : $MTIME;
+  my $options = {etag => md5_sum($mtime), last_modified => $mtime};
+  return $res->code(304) if $self->is_fresh($c, $options);
 
   # Range
-  my $size  = $asset->size;
-  my $start = 0;
-  my $end   = $size - 1;
-  if (my $range = $headers->range) {
+  my $size = $asset->size;
+  my ($start, $end) = (0, $size - 1);
+  if (my $range = $c->req->headers->range) {
 
     # Not satisfiable
     return $res->code(416) unless $size && $range =~ m/^bytes=(\d+)?-(\d+)?/;
@@ -94,24 +121,19 @@ sub serve_asset {
   return $res->content->asset($asset->start_range($start)->end_range($end));
 }
 
+sub _epoch { Mojo::Date->new(shift)->epoch }
+
 sub _get_data_file {
   my ($self, $rel) = @_;
 
   # Protect templates
   return undef if $rel =~ /\.\w+\.\w+$/;
 
-  # Index DATA files
-  my $loader = Mojo::Loader->new;
-  unless ($self->{index}) {
-    my $index = $self->{index} = {};
-    for my $class (reverse @{$self->classes}) {
-      $index->{$_} = $class for keys %{$loader->data($class)};
-    }
-  }
+  $self->_warmup unless $self->{index};
 
   # Find file
   return undef
-    unless defined(my $data = $loader->data($self->{index}{$rel}, $rel));
+    unless defined(my $data = $LOADER->data($self->{index}{$rel}, $rel));
   return Mojo::Asset::Memory->new->add_chunk($data);
 }
 
@@ -121,6 +143,14 @@ sub _get_file {
   return -f $path && -r $path ? Mojo::Asset::File->new(path => $path) : undef;
 }
 
+sub _warmup {
+  my $self = shift;
+  my $index = $self->{index} = {};
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for keys %{$LOADER->data($class)};
+  }
+}
+
 1;
 
 =encoding utf8
@@ -134,7 +164,7 @@ Mojolicious::Static - Serve static files
   use Mojolicious::Static;
 
   my $static = Mojolicious::Static->new;
-  push @{$static->classes}, 'MyApp::Foo';
+  push @{$static->classes}, 'MyApp::Controller::Foo';
   push @{$static->paths}, '/home/sri/public';
 
 =head1 DESCRIPTION
@@ -191,6 +221,32 @@ protect from traversing to parent directories.
 
   my $content = $static->file('foo/bar.html')->slurp;
 
+=head2 is_fresh
+
+  my $bool = $static->is_fresh(Mojolicious::Controller->new, {etag => 'abc'});
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers.
+
+These options are currently available:
+
+=over 2
+
+=item etag
+
+  etag => 'abc'
+
+Add C<ETag> header before comparing.
+
+=item last_modified
+
+  last_modified => $epoch
+
+Add C<Last-Modified> header before comparing.
+
+=back
+
 =head2 serve
 
   my $bool = $static->serve(Mojolicious::Controller->new, 'images/logo.png');
@@ -10,7 +10,7 @@ has [qw(input output)] => sub { {} };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
@@ -216,8 +216,8 @@
           <h1>Page not found... yet!</h1>
           <p>
             None of these routes could generate a response for your
-            <code><%= $self->req->method %></code> request for
-            <code><%= $self->req->url->path->to_route %></code>, maybe you need
+            <code><%= $c->req->method %></code> request for
+            <code><%= $c->req->url->path->to_route %></code>, maybe you need
             to add a new one?
           </p>
           % my $walk = begin
@@ -254,7 +254,7 @@
       % }
       <div id="request" class="box infobox spaced">
         <table>
-          % my $req = $self->req;
+          % my $req = $c->req;
           %= $kv->(Method => $req->method)
           % my $url = $req->url;
           %= $kv->(URL => $url->to_string)
@@ -263,8 +263,8 @@
           %= $kv->(Stash => dumper $snapshot)
           %= $kv->(Session => dumper session)
           %= $kv->(Version => $req->version)
-          % for my $name (sort @{$self->req->headers->names}) {
-            % my $value = $self->req->headers->header($name);
+          % for my $name (sort @{$c->req->headers->names}) {
+            % my $value = $c->req->headers->header($name);
             %= $kv->($name, $value)
           % }
         </table>
@@ -16,7 +16,7 @@
         line-height: 1.5em;
         margin: 0;
       }
-      code {
+      :not(pre) > code {
         background-color: rgba(0, 0, 0, 0.04);
         border-radius: 3px;
         font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
@@ -30,17 +30,24 @@
         position: relative;
       }
       h1 a, h2 a, h3 a { text-decoration: none }
+      li > p {
+        margin-bottom: 0;
+        margin-top: 0;
+      }
       pre {
         background: url(<%= url_for '/mojo/stripes.png' %>);
         border: 1px solid #d1d1d1;
         border-radius: 3px;
         box-shadow: 0 1px #fff, inset -1px 1px 4px rgba(0, 0, 0, 0.1);
-        color: #4d4d4c;
-        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
-        line-height: 1.5em;
+        font: 100% Consolas, Menlo, Monaco, Courier, monospace;
         padding: 1em;
         padding-bottom: 1.5em;
         padding-top: 1.5em;
+      }
+      pre > code {
+        color: #4d4d4c;
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        line-height: 1.5em;
         text-align: left;
         text-shadow: #eee 0 1px 0;
         white-space: pre-wrap;
@@ -43,18 +43,18 @@ has types     => sub { Mojolicious::Types->new };
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Tiger Face';
-our $VERSION  = '5.16';
+our $VERSION  = '5.33';
 
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
   # Call helper with fresh controller
   croak qq{Can't locate object method "$method" via package "$package"}
-    unless my $helper = $self->renderer->helpers->{$method};
+    unless my $helper = $self->renderer->get_helper($method);
   return $self->build_controller->$helper(@_);
 }
 
@@ -68,7 +68,8 @@ sub build_controller {
   $stash->{'mojo.secrets'} //= $self->secrets;
 
   # Build default controller
-  %$stash = (%$stash, %{$self->defaults});
+  my $defaults = $self->defaults;
+  @$stash{keys %$defaults} = values %$defaults;
   my $c
     = $self->controller_class->new(app => $self, stash => $stash, tx => $tx);
   weaken $c->{app};
@@ -83,7 +84,7 @@ sub build_tx {
   return $tx;
 }
 
-sub defaults { shift->_dict(defaults => @_) }
+sub defaults { Mojo::Util::_stash(defaults => @_) }
 
 sub dispatch {
   my ($self, $c) = @_;
@@ -153,15 +154,15 @@ sub new {
   push @{$self->renderer->paths}, $home->rel_dir('templates');
   push @{$self->static->paths},   $home->rel_dir('public');
 
-  # Default to application namespace
-  my $r = $self->routes->namespaces([ref $self]);
+  # Default to controller and application namespace
+  my $r = $self->routes->namespaces(["@{[ref $self]}::Controller", ref $self]);
 
   # Hide controller attributes/methods and "handler"
-  $r->hide(qw(app continue cookie finish flash handler match on param));
-  $r->hide(qw(redirect_to render render_exception render_later render_maybe));
-  $r->hide(qw(render_not_found render_static render_to_string rendered req));
-  $r->hide(qw(res respond_to send session signed_cookie stash tx url_for));
-  $r->hide(qw(validation write write_chunk));
+  $r->hide(qw(app continue cookie finish flash handler helpers match on));
+  $r->hide(qw(param redirect_to render render_exception render_later));
+  $r->hide(qw(render_maybe render_not_found render_static render_to_string));
+  $r->hide(qw(rendered req res respond_to send session signed_cookie stash));
+  $r->hide(qw(tx url_for validation write write_chunk));
 
   # Check if we have a log directory
   my $mode = $self->mode;
@@ -187,7 +188,11 @@ sub plugin {
   $self->plugins->register_plugin(shift, $self, @_);
 }
 
-sub start { shift->commands->run(@_ ? @_ : @ARGV) }
+sub start {
+  my $self = shift;
+  $_->_warmup for $self->static, $self->renderer;
+  return $self->commands->run(@_ ? @_ : @ARGV);
+}
 
 sub startup { }
 
@@ -219,7 +224,7 @@ Mojolicious - Real-time web framework
   }
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -456,7 +461,7 @@ startup method to define the url endpoints for your application.
   $r->post('/baz')->to('test#baz');
 
   # Add another namespace to load controllers from
-  push @{$app->routes->namespaces}, 'MyApp::Controller';
+  push @{$app->routes->namespaces}, 'MyApp::MyController';
 
 =head2 secrets
 
@@ -737,6 +742,8 @@ Abhijit Menon-Sen, C<ams@cpan.org>
 
 Glen Hinkle, C<tempire@cpan.org>
 
+Jan Henning Thorsen, C<jhthorsen@cpan.org>
+
 Joel Berger, C<jberger@cpan.org>
 
 Marcus Ramberg, C<mramberg@cpan.org>
@@ -853,8 +860,6 @@ Ilya Chesnokov
 
 James Duncan
 
-Jan Henning Thorsen
-
 Jan Jona Javorsek
 
 Jan Schmidt
@@ -591,9 +591,9 @@ Opposite of L</"content_type_like">.
 =head2 delete_ok
 
   $t = $t->delete_ok('/foo');
-  $t = $t->delete_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->delete_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->delete_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<DELETE> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"delete">, except for the callback.
@@ -630,9 +630,9 @@ Wait for WebSocket connection to be closed gracefully and check status.
 =head2 get_ok
 
   $t = $t->get_ok('/foo');
-  $t = $t->get_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->get_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->get_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<GET> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"get">, except for the callback.
@@ -643,9 +643,9 @@ arguments as L<Mojo::UserAgent/"get">, except for the callback.
 =head2 head_ok
 
   $t = $t->head_ok('/foo');
-  $t = $t->head_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->head_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->head_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<HEAD> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"head">, except for the callback.
@@ -818,9 +818,9 @@ Construct a new L<Test::Mojo> object.
 =head2 options_ok
 
   $t = $t->options_ok('/foo');
-  $t = $t->options_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->options_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->options_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<OPTIONS> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"options">, except for the callback.
@@ -838,9 +838,9 @@ Invoke callback if the value of L</"success"> is false.
 =head2 patch_ok
 
   $t = $t->patch_ok('/foo');
-  $t = $t->patch_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->patch_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->patch_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<PATCH> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"patch">, except for the callback.
@@ -848,9 +848,9 @@ arguments as L<Mojo::UserAgent/"patch">, except for the callback.
 =head2 post_ok
 
   $t = $t->post_ok('/foo');
-  $t = $t->post_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->post_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->post_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<POST> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"post">, except for the callback.
@@ -867,9 +867,9 @@ arguments as L<Mojo::UserAgent/"post">, except for the callback.
 =head2 put_ok
 
   $t = $t->put_ok('/foo');
-  $t = $t->put_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->put_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->put_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<PUT> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"put">, except for the callback.
@@ -1,6 +1,7 @@
 package ojo;
 use Mojo::Base -strict;
 
+use Benchmark qw(timeit timestr :hireswallclock);
 use Mojo::ByteStream 'b';
 use Mojo::Collection 'c';
 use Mojo::DOM;
@@ -14,7 +15,7 @@ sub import {
 
   # Mojolicious::Lite
   my $caller = caller;
-  eval "package $caller; use Mojolicious::Lite;";
+  eval "package $caller; use Mojolicious::Lite; 1" or die $@;
   my $ua = $caller->app->ua;
   $ua->server->app->hook(around_action => sub { local $_ = $_[1]; $_[0]->() });
 
@@ -26,10 +27,11 @@ sub import {
     a => sub { $caller->can('any')->(@_) and return $ua->server->app },
     b => \&b,
     c => \&c,
-    d => sub { _request($ua, 'DELETE',  @_) },
-    g => sub { _request($ua, 'GET',     @_) },
-    h => sub { _request($ua, 'HEAD',    @_) },
+    d => sub { _request($ua, 'DELETE', @_) },
+    g => sub { _request($ua, 'GET',    @_) },
+    h => sub { _request($ua, 'HEAD',   @_) },
     j => \&j,
+    n => sub (&@) { say STDERR timestr timeit($_[1] // 1, $_[0]) },
     o => sub { _request($ua, 'OPTIONS', @_) },
     p => sub { _request($ua, 'POST',    @_) },
     r => \&dumper,
@@ -111,7 +113,7 @@ Turn list into a L<Mojo::Collection> object.
 =head2 d
 
   my $res = d('example.com');
-  my $res = d('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = d('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<DELETE> request with L<Mojo::UserAgent/"delete"> and return
 resulting L<Mojo::Message::Response> object.
@@ -119,7 +121,7 @@ resulting L<Mojo::Message::Response> object.
 =head2 g
 
   my $res = g('example.com');
-  my $res = g('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = g('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<GET> request with L<Mojo::UserAgent/"get"> and return resulting
 L<Mojo::Message::Response> object.
@@ -129,7 +131,7 @@ L<Mojo::Message::Response> object.
 =head2 h
 
   my $res = h('example.com');
-  my $res = h('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = h('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<HEAD> request with L<Mojo::UserAgent/"head"> and return resulting
 L<Mojo::Message::Response> object.
@@ -144,10 +146,20 @@ Encode Perl data structure or decode JSON with L<Mojo::JSON/"j">.
 
   $ perl -Mojo -E 'b(j({hello => "world!"}))->spurt("hello.json")'
 
+=head2 n
+
+  n {...};
+  n {...} 100;
+
+Benchmark block and print the results to C<STDERR>, with an optional number of
+iterations, which defaults to C<1>.
+
+  $ perl -Mojo -E 'n { say g("mojolicio.us")->code }'
+
 =head2 o
 
   my $res = o('example.com');
-  my $res = o('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = o('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<OPTIONS> request with L<Mojo::UserAgent/"options"> and return
 resulting L<Mojo::Message::Response> object.
@@ -155,7 +167,7 @@ resulting L<Mojo::Message::Response> object.
 =head2 p
 
   my $res = p('example.com');
-  my $res = p('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = p('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<POST> request with L<Mojo::UserAgent/"post"> and return resulting
 L<Mojo::Message::Response> object.
@@ -171,7 +183,7 @@ Dump a Perl data structure with L<Mojo::Util/"dumper">.
 =head2 t
 
   my $res = t('example.com');
-  my $res = t('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = t('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<PATCH> request with L<Mojo::UserAgent/"patch"> and return resulting
 L<Mojo::Message::Response> object.
@@ -179,7 +191,7 @@ L<Mojo::Message::Response> object.
 =head2 u
 
   my $res = u('example.com');
-  my $res = u('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = u('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<PUT> request with L<Mojo::UserAgent/"put"> and return resulting
 L<Mojo::Message::Response> object.
@@ -9,6 +9,8 @@ use Mojo::Asset::Memory;
 
 # File asset
 my $file = Mojo::Asset::File->new;
+is $file->size,  0,  'file is empty';
+is $file->slurp, '', 'file is empty';
 $file->add_chunk('abc');
 is $file->contains('abc'), 0,  '"abc" at position 0';
 is $file->contains('bc'),  1,  '"bc" at position 1';
@@ -112,9 +112,17 @@ is $collection->size, 1, 'right size';
 $collection = c(5, 4, 3, 2, 1);
 is $collection->size, 5, 'right size';
 
+# reduce
+$collection = c(2, 5, 4, 1);
+is $collection->reduce(sub { $a + $b }), 12, 'right result';
+is $collection->reduce(sub { $a + $b }, 5), 17, 'right result';
+is c()->reduce(sub { $a + $b }), undef, 'no result';
+
 # sort
 $collection = c(2, 5, 4, 1);
 is_deeply [$collection->sort->each], [1, 2, 4, 5], 'right order';
+is_deeply [$collection->sort(sub { $b cmp $a })->each], [5, 4, 2, 1],
+  'right order';
 is_deeply [$collection->sort(sub { $_[1] cmp $_[0] })->each], [5, 4, 2, 1],
   'right order';
 $collection = c(qw(Test perl Mojo));
@@ -122,8 +130,7 @@ is_deeply [$collection->sort(sub { uc(shift) cmp uc(shift) })->each],
   [qw(Mojo perl Test)], 'right order';
 $collection = c();
 is_deeply [$collection->sort->each], [], 'no elements';
-is_deeply [$collection->sort(sub { $_[1] cmp $_[0] })->each], [],
-  'no elements';
+is_deeply [$collection->sort(sub { $a cmp $b })->each], [], 'no elements';
 
 # slice
 $collection = c(1, 2, 3, 4, 5, 6, 7, 10, 9, 8);
@@ -7,6 +7,7 @@ BEGIN {
 
 use Test::More;
 use File::Spec::Functions 'catdir';
+use FindBin;
 use Mojo;
 use Mojo::IOLoop;
 use Mojo::Log;
@@ -61,6 +62,13 @@ is $app->config({test => 23})->config->{test}, 23, 'right value';
 is_deeply $app->config, {foo => 'bar', baz => 'yada', test => 23},
   'right value';
 
+# Load broken app
+eval {
+  Mojo::Server::Daemon->new->load_app(
+    "$FindBin::Bin/lib/Mojo/LoaderException.pm");
+};
+like $@, qr/^Couldn't load application/, 'right error';
+
 # Transaction
 isa_ok $app->build_tx, 'Mojo::Transaction::HTTP', 'right class';
 
@@ -9,6 +9,30 @@ is $date->epoch, 784111777, 'right epoch value';
 $date = Mojo::Date->new('Fri, 13 May 2011 10:00:24 GMT');
 is $date->epoch, 1305280824, 'right epoch value';
 
+# RFC 3339
+is(Mojo::Date->new('2014-08-20T20:45:00')->epoch,
+  1408567500, 'right epoch value');
+is(Mojo::Date->new(1408567500)->to_datetime,
+  '2014-08-20T20:45:00Z', 'right format');
+is(Mojo::Date->new('2014-08-20T20:45:00.01')->epoch,
+  1408567500.01, 'right epoch value');
+is(Mojo::Date->new('2014-08-20T20:45:00-00:46')->epoch,
+  1408570260, 'right epoch value');
+is(Mojo::Date->new(1408570260)->to_datetime,
+  '2014-08-20T21:31:00Z', 'right format');
+is(Mojo::Date->new('2014-08-20t20:45:00-01:46')->epoch,
+  1408573860, 'right epoch value');
+is(Mojo::Date->new('2014-08-20t20:45:00+01:46')->epoch,
+  1408561140, 'right epoch value');
+is(Mojo::Date->new(1408561140)->to_datetime,
+  '2014-08-20T18:59:00Z', 'right format');
+is(Mojo::Date->new('1994-11-06T08:49:37Z')->epoch,
+  784111777, 'right epoch value');
+is(Mojo::Date->new('1994-11-06t08:49:37.33z')->epoch,
+  784111777.33, 'right epoch value');
+is(Mojo::Date->new(784111777.33)->to_datetime,
+  '1994-11-06T08:49:37.33Z', 'right format');
+
 # RFC 850/1036
 is(Mojo::Date->new('Sunday, 06-Nov-94 08:49:37 GMT')->epoch,
   784111777, 'right epoch value');
@@ -35,6 +59,8 @@ is(Mojo::Date->new('Sun Nov  6 08:49:37 1994 GARBAGE')->epoch,
   undef, 'no epoch value');
 is(Mojo::Date->new('Fri, 75 May 2011 99:99:99 GMT')->epoch,
   undef, 'no epoch value');
+is(Mojo::Date->new('0000-00-00T00:00:00+01:00')->epoch,
+  undef, 'no epoch value');
 
 # to_string
 $date = Mojo::Date->new(784111777);
@@ -42,6 +68,13 @@ is "$date", 'Sun, 06 Nov 1994 08:49:37 GMT', 'right format';
 $date = Mojo::Date->new(1305280824);
 is $date->to_string, 'Fri, 13 May 2011 10:00:24 GMT', 'right format';
 
+# Current time roundtrips
+my $before = time;
+ok(Mojo::Date->new(Mojo::Date->new->to_string)->epoch >= $before,
+  'successful roundtrip');
+ok(Mojo::Date->new(Mojo::Date->new->to_datetime)->epoch >= $before,
+  'successful roundtrip');
+
 # Zero time checks
 $date = Mojo::Date->new(0);
 is $date->epoch, 0, 'right epoch value';
@@ -2303,12 +2303,59 @@ is $dom->find('div > ul li')->[2], undef, 'no result';
 is $dom->find('div > ul ul')->[0]->text, 'C', 'right text';
 is $dom->find('div > ul ul')->[1], undef, 'no result';
 
+# Form values
+$dom = Mojo::DOM->new(<<EOF);
+<form action="/foo">
+  <p>Test</p>
+  <input type="text" name="a" value="A" />
+  <input type="checkbox" checked name="b" value="B">
+  <input type="radio" checked name="c" value="C">
+  <select name="f">
+    <option value="F">G</option>
+    <optgroup>
+      <option>H</option>
+      <option selected>I</option>
+    </optgroup>
+    <option value="J" selected>K</option>
+  </select>
+  <select name="n"><option>N</option></select>
+  <select name="d"><option selected>D</option></select>
+  <textarea name="m">M</textarea>
+  <button name="o" value="O">No!</button>
+  <input type="submit" name="p" value="P" />
+</form>
+EOF
+is_deeply [$dom->at('p')->val->each], [], 'no values';
+is $dom->at('input')->val->size, 1, 'one value';
+is $dom->at('input')->val,                     'A', 'right value';
+is $dom->at('input:checked')->val,             'B', 'right value';
+is $dom->at('input:checked[type=radio]')->val, 'C', 'right value';
+is $dom->find('select')->first->val->join(':'), 'I:J', 'right value';
+is_deeply [$dom->find('select')->first->val->each], ['I', 'J'], 'right values';
+is $dom->at('select option')->val->size, 1, 'one value';
+is $dom->at('select option')->val,                          'F', 'right value';
+is $dom->at('select optgroup option:not([selected])')->val, 'H', 'right value';
+is $dom->find('select')->[1]->val->size, 0, 'no values';
+is $dom->find('select')->[1]->at('option')->val, 'N', 'right value';
+is $dom->find('select')->last->val, 'D', 'right value';
+is $dom->at('textarea')->val->size, 1,   'one value';
+is $dom->at('textarea')->val, 'M', 'right value';
+is $dom->at('button')->val,   'O', 'right value';
+is $dom->find('form input')->last->val, 'P', 'right value';
+
 # Slash between attributes
 $dom = Mojo::DOM->new('<input /type=checkbox / value="/a/" checked/><br/>');
 is_deeply $dom->at('input')->attr,
   {type => 'checkbox', value => '/a/', checked => undef}, 'right attributes';
 is "$dom", '<input checked type="checkbox" value="/a/"><br>', 'right result';
 
+# Dot and hash in class and id attributes
+$dom = Mojo::DOM->new('<p class="a#b.c">A</p><p id="a#b.c">B</p>');
+is $dom->at('p.a\#b\.c')->text,       'A', 'right text';
+is $dom->at(':not(p.a\#b\.c)')->text, 'B', 'right text';
+is $dom->at('p#a\#b\.c')->text,       'B', 'right text';
+is $dom->at(':not(p#a\#b\.c)')->text, 'A', 'right text';
+
 # Extra whitespace
 $dom = Mojo::DOM->new('< span>a< /span><b >b</b><span >c</ span>');
 is $dom->at('span')->text,     'a', 'right text';
@@ -147,7 +147,7 @@ is b($bytes)->decode('UTF-8'), "[\"hello\\u0003\x{0152}world\x{0152}!\"]",
 $bytes = encode_json ["123abc"];
 is $bytes, '["123abc"]', 'encode ["123abc"]';
 $bytes = encode_json ["\x00\x1f \a\b/\f\r"];
-is $bytes, '["\\u0000\\u001F \\u0007\\b\/\f\r"]',
+is $bytes, '["\\u0000\\u001F \\u0007\\b/\f\r"]',
   'encode ["\x00\x1f \a\b/\f\r"]';
 $bytes = encode_json '';
 is $bytes, '""', 'encode ""';
@@ -64,8 +64,6 @@ is $res->code(428)->default_message, 'Precondition Required', 'right message';
 is $res->code(429)->default_message, 'Too Many Requests',     'right message';
 is $res->code(431)->default_message, 'Request Header Fields Too Large',
   'right message';
-is $res->code(451)->default_message, 'Unavailable For Legal Reasons',
-  'right message';
 is $res->code(500)->default_message, 'Internal Server Error', 'right message';
 is $res->code(501)->default_message, 'Not Implemented',       'right message';
 is $res->code(502)->default_message, 'Bad Gateway',           'right message';
@@ -326,6 +326,13 @@ is(($t->endpoint($tx))[0], 'http',      'right scheme');
 is(($t->endpoint($tx))[1], '127.0.0.1', 'right host');
 is(($t->endpoint($tx))[2], 3000,        'right port');
 
+# Simple endpoint with SOCKS proxy
+$tx = $t->tx(GET => 'http://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->endpoint($tx))[0], 'http',         'right scheme');
+is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
+is(($t->endpoint($tx))[2], 80,             'right port');
+
 # Simple WebSocket endpoint with proxy
 $tx = $t->websocket('ws://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -346,6 +353,13 @@ is(($t->endpoint($tx))[0], 'https',        'right scheme');
 is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
 is(($t->endpoint($tx))[2], 443,            'right port');
 
+# HTTPS endpoint with SOCKS proxy
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->endpoint($tx))[0], 'https',        'right scheme');
+is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
+is(($t->endpoint($tx))[2], 443,            'right port');
+
 # TLS WebSocket endpoint with proxy
 $tx = $t->websocket('WSS://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -366,6 +380,13 @@ is(($t->peer($tx))[0], 'http',      'right scheme');
 is(($t->peer($tx))[1], '127.0.0.1', 'right host');
 is(($t->peer($tx))[2], 3000,        'right port');
 
+# Simple peer with SOCKS proxy
+$tx = $t->tx(GET => 'http://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->peer($tx))[0], 'socks',     'right scheme');
+is(($t->peer($tx))[1], '127.0.0.1', 'right host');
+is(($t->peer($tx))[2], 3000,        'right port');
+
 # Simple peer with proxy (no port)
 $tx = $t->tx(GET => 'http://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1');
@@ -400,6 +421,13 @@ is(($t->peer($tx))[0], 'http',      'right scheme');
 is(($t->peer($tx))[1], '127.0.0.1', 'right host');
 is(($t->peer($tx))[2], 3000,        'right port');
 
+# HTTPS peer with SOCKS proxy
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->peer($tx))[0], 'socks',     'right scheme');
+is(($t->peer($tx))[1], '127.0.0.1', 'right host');
+is(($t->peer($tx))[2], 3000,        'right port');
+
 # TLS WebSocket peer with proxy
 $tx = $t->websocket('wss://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -494,6 +522,9 @@ is $tx->req->headers->host, 'mojolicio.us', 'right "Host" header';
 is $t->proxy_connect($tx), undef, 'already a CONNECT request';
 $tx->req->method('Connect');
 is $t->proxy_connect($tx), undef, 'already a CONNECT request';
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is $t->proxy_connect($tx), undef, 'using a SOCKS proxy';
 
 # Simple 301 redirect
 $tx = $t->tx(
@@ -1,7 +1,7 @@
 use Mojo::Base -strict;
 
 BEGIN {
-  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_TLS} = 1;
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_SOCKS} = $ENV{MOJO_NO_TLS} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -125,10 +125,17 @@ ok $success, 'successful';
 is $code,    200, 'right status';
 is $body,    'works!', 'right content';
 
+# SOCKS proxy request without SOCKS support
+$ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+my $tx = $ua->build_tx(GET => '/');
+$tx->req->proxy($ua->server->url->scheme('socks'));
+$tx = $ua->start($tx);
+like $tx->error->{message}, qr/IO::Socket::Socks/, 'right error';
+
 # HTTPS request without TLS support
 $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
-my $tx = $ua->get($ua->server->url->scheme('https'));
-ok $tx->error, 'has error';
+$tx = $ua->get($ua->server->url->scheme('https'));
+like $tx->error->{message}, qr/IO::Socket::SSL/, 'right error';
 
 # Blocking
 $tx = $ua->get('/');
@@ -0,0 +1,172 @@
+use Mojo::Base -strict;
+
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = 1;
+  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
+}
+
+use Test::More;
+use Mojo::IOLoop::Client;
+
+plan skip_all => 'set TEST_SOCKS to enable this test (developer only!)'
+  unless $ENV{TEST_SOCKS};
+plan skip_all => 'IO::Socket::Socks 0.64 required for this test!'
+  unless Mojo::IOLoop::Client::SOCKS;
+plan skip_all => 'IO::Socket::SSL 1.84 required for this test!'
+  unless Mojo::IOLoop::Server::TLS;
+
+use Mojo::IOLoop;
+use Mojo::IOLoop::Server;
+use Mojo::IOLoop::Stream;
+use Mojo::UserAgent;
+use Mojolicious::Lite;
+use Scalar::Util 'weaken';
+
+# Silence
+app->log->level('fatal');
+
+get '/' => sub {
+  my $c = shift;
+  $c->render(text => $c->tx->remote_port);
+};
+
+websocket '/echo' => sub {
+  my $c = shift;
+  $c->on(
+    message => sub {
+      my $c = shift;
+      $c->send($c->tx->remote_port);
+    }
+  );
+};
+
+get '/secure' => sub {
+  my $c = shift;
+  $c->render(
+    text => $c->req->url->to_abs->protocol . ':' . $c->tx->remote_port);
+};
+
+my $port   = Mojo::IOLoop::Server->generate_port;
+my $server = IO::Socket::Socks->new(
+  Blocking    => 0,
+  Listen      => 10,
+  ProxyAddr   => '127.0.0.1',
+  ProxyPort   => $port,
+  RequireAuth => 1,
+  UserAuth    => sub { $_[0] eq 'foo' && $_[1] eq 'bar' }
+);
+
+# SOCKS proxy server for testing
+my $last;
+Mojo::IOLoop->singleton->reactor->io(
+  $server => sub {
+    my $reactor = shift;
+
+    my $client = $server->accept;
+    $client->blocking(0);
+    my ($address, $port);
+    $reactor->io(
+      $client => sub {
+        my $reactor = shift;
+
+        my $err = $IO::Socket::Socks::SOCKS_ERROR;
+        if ($client->ready) {
+
+          if ($address) {
+            $reactor->remove($client);
+            Mojo::IOLoop->client(
+              {address => $address, port => $port} => sub {
+                my ($loop, $err, $server) = @_;
+                $last = $server->handle->sockport;
+                weaken $server;
+                $client = Mojo::IOLoop::Stream->new($client);
+                Mojo::IOLoop->stream($client);
+                $client->on(read  => sub { $server->write(pop) });
+                $client->on(close => sub { $server && $server->close });
+                $server->on(read  => sub { $client->write(pop) });
+                $server->on(close => sub { $client && $client->close });
+              }
+            );
+          }
+
+          else {
+            ($address, $port) = @{$client->command}[1, 2];
+            $client->command_reply(IO::Socket::Socks::REPLY_SUCCESS(),
+              $address, $port);
+          }
+        }
+        elsif ($err == IO::Socket::Socks::SOCKS_WANT_WRITE()) {
+          $reactor->watch($client, 1, 1);
+        }
+        elsif ($err == IO::Socket::Socks::SOCKS_WANT_READ()) {
+          $reactor->watch($client, 1, 0);
+        }
+      }
+    );
+  }
+);
+
+# Failed authentication with SOCKS proxy
+my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+$ua->proxy->http("socks://foo:baz\@127.0.0.1:$port");
+my $tx = $ua->get('/');
+ok !$tx->success, 'not successful';
+ok $tx->error, 'has error';
+
+# Simple request with SOCKS proxy
+$ua->proxy->http("socks://foo:bar\@127.0.0.1:$port");
+$tx = $ua->get('/');
+ok $tx->success, 'successful';
+ok !$tx->kept_alive, 'kept connection not alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200, 'right status';
+is $tx->req->headers->proxy_authorization, undef,
+  'no "Proxy-Authorization" value';
+is $tx->res->body, $last, 'right content';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+# Keep alive request with SOCKS proxy
+my $before = $last;
+$tx = $ua->get('/');
+ok $tx->success,    'successful';
+ok $tx->kept_alive, 'kept connection alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200, 'right status';
+is $tx->res->body, $last, 'right content';
+is $before, $last, 'same port';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+# WebSocket with SOCKS proxy
+my ($result, $id);
+$ua->websocket(
+  '/echo' => sub {
+    my ($ua, $tx) = @_;
+    $id = $tx->connection;
+    $tx->on(
+      message => sub {
+        $result = pop;
+        Mojo::IOLoop->stop;
+      }
+    );
+    $tx->send('test');
+  }
+);
+Mojo::IOLoop->start;
+is $result, $last, 'right result';
+isnt(Mojo::IOLoop->stream($id)->handle->sockport, $last, 'different ports');
+
+# HTTPS request with SOCKS proxy
+$ua->proxy->https("socks://foo:bar\@127.0.0.1:$port");
+$ua->server->url('https');
+$tx = $ua->get('/secure');
+ok $tx->success, 'successful';
+ok !$tx->kept_alive, 'kept connection not alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200,           'right status';
+is $tx->res->body, "https:$last", 'right content';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+done_testing();
@@ -67,8 +67,8 @@ is_deeply split_header('f "o" o , ba  r'),
   'right result';
 is_deeply split_header('foo="b,; a\" r\"\\\\"'), [['foo', 'b,; a" r"\\']],
   'right result';
-is_deeply split_header('foo = "b a\" r\"\\\\"'), [['foo', 'b a" r"\\']],
-  'right result';
+is_deeply split_header('foo = "b a\" r\"\\\\"; bar="ba z"'),
+  [['foo', 'b a" r"\\', 'bar', 'ba z']], 'right result';
 my $header = q{</foo/bar>; rel="x"; t*=UTF-8'de'a%20b};
 my $tree = [['</foo/bar>', undef, 'rel', 'x', 't*', 'UTF-8\'de\'a%20b']];
 is_deeply split_header($header), $tree, 'right result';
@@ -29,6 +29,19 @@ is $frame->[4], 1,          'text frame';
 is $frame->[5], 'whatever', 'right payload';
 is $ws->build_frame(1, 1, 1, 1, 1, 'whatever'), $bytes, 'frames are equal';
 
+# Simple text frame roundtrip without FIN bit
+$ws = Mojo::Transaction::WebSocket->new;
+$bytes = $ws->build_frame(0, 0, 0, 0, 1, 'whatever');
+is $bytes, "\x01\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame';
+$frame = $ws->parse_frame(\($dummy = $bytes));
+is $frame->[0], 0,          'fin flag is not set';
+is $frame->[1], 0,          'rsv1 flag is not set';
+is $frame->[2], 0,          'rsv2 flag is not set';
+is $frame->[3], 0,          'rsv3 flag is not set';
+is $frame->[4], 1,          'text frame';
+is $frame->[5], 'whatever', 'right payload';
+is $ws->build_frame(0, 0, 0, 0, 1, 'whatever'), $bytes, 'frames are equal';
+
 # Simple text frame roundtrip with RSV1 flags set
 $ws = Mojo::Transaction::WebSocket->new;
 $bytes = $ws->build_frame(1, 1, 0, 0, 1, 'whatever');
@@ -139,6 +139,19 @@ my $id = Mojo::IOLoop->server(
 );
 my $proxy = Mojo::IOLoop->acceptor($id)->handle->sockport;
 
+# Fake server to test failed TLS handshake
+$id = Mojo::IOLoop->server(
+  sub {
+    my ($loop, $stream) = @_;
+    $stream->on(read => sub { shift->close });
+  }
+);
+my $close = Mojo::IOLoop->acceptor($id)->handle->sockport;
+
+# Fake server to test idle connection
+$id = Mojo::IOLoop->server(sub { });
+my $idle = Mojo::IOLoop->acceptor($id)->handle->sockport;
+
 # User agent with valid certificates
 my $ua = Mojo::UserAgent->new(
   ioloop => Mojo::IOLoop->singleton,
@@ -272,7 +285,7 @@ $ua->websocket(
   "wss://localhost:$port2/test" => sub {
     my ($ua, $tx) = @_;
     $success = $tx->success;
-    $err     = $tx->error;
+    $err     = $tx->res->error;
     Mojo::IOLoop->stop;
   }
 );
@@ -280,8 +293,18 @@ Mojo::IOLoop->start;
 ok !$success, 'no success';
 is $err->{message}, 'Proxy connection failed', 'right error';
 
+# Failed TLS handshake through proxy
+$tx = $ua->get("https://localhost:$close");
+is $err->{message}, 'Proxy connection failed', 'right error';
+
+# Idle connection through proxy
+$ua->on(start =>
+    sub { shift->connect_timeout(0.25) if pop->req->method eq 'CONNECT' });
+$tx = $ua->get("https://localhost:$idle");
+is $err->{message}, 'Proxy connection failed', 'right error';
+$ua->connect_timeout(10);
+
 # Blocking proxy request again
-$ua->proxy->https("http://localhost:$proxy");
 $tx = $ua->get("https://localhost:$port/proxy");
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, "https://localhost:$port/proxy", 'right content';
@@ -104,6 +104,7 @@ ok $t->app->routes->is_hidden('finish'),           'is hidden';
 ok $t->app->routes->is_hidden('flash'),            'is hidden';
 ok $t->app->routes->is_hidden('handler'),          'is hidden';
 ok $t->app->routes->is_hidden('has'),              'is hidden';
+ok $t->app->routes->is_hidden('helpers'),          'is hidden';
 ok $t->app->routes->is_hidden('match'),            'is hidden';
 ok $t->app->routes->is_hidden('new'),              'is hidden';
 ok $t->app->routes->is_hidden('on'),               'is hidden';
@@ -224,8 +225,7 @@ like $log, qr/Rendering template "syntaxerror.html.epl"\./, 'right message';
 like $log, qr/Missing right curly/, 'right message';
 like $log, qr/Template "exception.development.html.ep" not found\./,
   'right message';
-like $log, qr/Rendering cached template "exception.html.epl"\./,
-  'right message';
+like $log, qr/Rendering template "exception.html.epl"\./, 'right message';
 like $log, qr/500 Internal Server Error/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -259,10 +259,16 @@ $t->get_ok('/fun/time' => {'X-Test' => 'Hi there!'})->status_is(200)
 
 # Foo::fun
 $url = $t->ua->server->url;
+$log = '';
+$cb  = $t->app->log->on(message => sub { $log .= pop });
 $url->path('/fun/time');
 $t->get_ok($url => {'X-Test' => 'Hi there!'})->status_is(200)
   ->header_is('X-Bender' => undef)->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('Have fun!');
+like $log,
+  qr!Rendering cached template "foo/fun\.html\.ep" from DATA section\.!,
+  'right message';
+$t->app->log->unsubscribe(message => $cb);
 
 # Foo::fun
 $t->get_ok('/happy/fun/time' => {'X-Test' => 'Hi there!'})->status_is(200)
@@ -374,8 +380,9 @@ my $mtime = Mojo::Date->new((stat $path)[9])->to_string;
 
 # Static file /hello.txt
 $t->get_ok('/hello.txt')->status_is(200)
-  ->header_is(Server => 'Mojolicious (Perl)')
-  ->header_is('Last-Modified' => $mtime)->header_is('Content-Length' => $size)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Last-Modified'  => $mtime)->header_like('ETag' => qr/^"\w+"$/)
+  ->header_is('Content-Length' => $size)
   ->content_type_is('text/plain;charset=UTF-8')
   ->content_like(qr/Hello Mojo from a development static file!/);
 
@@ -389,6 +396,28 @@ $t->get_ok('/../../mojolicious/secret.txt')->status_is(404)
 $t->get_ok('/hello.txt' => {'If-Modified-Since' => $mtime})->status_is(304)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
 
+# Check If-None-Match
+my $etag = $t->tx->res->headers->etag;
+$t->get_ok('/hello.txt' => {'If-None-Match' => $etag})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Check If-None-Match and If-Last-Modified
+$t->get_ok(
+  '/hello.txt' => {'If-None-Match' => $etag, 'If-Last-Modified' => $mtime})
+  ->status_is(304)->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Bad If-None-Match with correct If-Modified-Since
+$t->get_ok(
+  '/hello.txt' => {'If-None-Match' => '"123"', 'If-Modified-Since' => $mtime})
+  ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->content_like(qr/Hello Mojo from a development static file!/);
+
+# Bad If-Modified-Since with correct If-None-Match
+$t->get_ok('/hello.txt' =>
+    {'If-Modified-Since' => Mojo::Date->new(23), 'If-None-Match' => $etag})
+  ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->content_like(qr/Hello Mojo from a development static file!/);
+
 # Embedded development static file
 $t->get_ok('/some/static/file.txt')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
@@ -38,6 +38,12 @@ is app->test_helper2, 'Mojolicious::Controller', 'right value';
 app->test_helper3->{foo} = 'bar';
 is app->test_helper3->{foo}, 'bar', 'right result';
 
+# Nested helpers
+helper 'test.helper' => sub { shift->app->controller_class };
+is app->test->helper, 'Mojolicious::Controller', 'right value';
+is app->build_controller->test->helper, 'Mojolicious::Controller',
+  'right value';
+
 # Test renderer
 app->renderer->add_handler(dead => sub { die 'renderer works!' });
 
@@ -474,6 +480,8 @@ is $t->app->build_controller($t->app->ua->build_tx(GET => '/foo'))->req->url,
   '/foo', 'right URL';
 is $t->app->build_controller->render_to_string('index', handler => 'epl'),
   'Just works!', 'right result';
+is $t->app->build_controller->render_to_string(inline => '0'), "0\n",
+  'right result';
 
 # Unicode snowman
 $t->get_ok('/☃')->status_is(200)
@@ -49,4 +49,18 @@ is c(1, 2, 3)->join('-'), '1-2-3', 'right result';
 # Dumper
 is r([1, 2]), "[\n  1,\n  2\n]\n", 'right result';
 
+# Benchmark
+{
+  my $buffer = '';
+  open my $handle, '>', \$buffer;
+  local *STDERR = $handle;
+  my $i = 0;
+  n { ++$i };
+  is $i,        1,             'block has been invoked once';
+  like $buffer, qr/wallclock/, 'right output';
+  n { $i++ } 10;
+  is $i, 11, 'block has been invoked ten times';
+  like $buffer, qr/wallclock.*wallclock/s, 'right output';
+}
+
 done_testing();
@@ -55,6 +55,7 @@ is $pattern->render({a => 'c', b => 'd'}), '/test/c/123/d/456', 'right result';
 
 # Root
 $pattern = Mojolicious::Routes::Pattern->new('/');
+is $pattern->pattern, undef, 'slash has been optimized away';
 $pattern->defaults({action => 'index'});
 ok !$pattern->match('/test/foo/bar'), 'no result';
 is_deeply $pattern->match('/'), {action => 'index'}, 'right structure';
@@ -252,6 +253,13 @@ $result = $pattern->match('/0', 1);
 is_deeply $result, {}, 'right structure';
 is $pattern->render($result, 1), '/0', 'right result';
 
+# Optional format with constraint
+$pattern                        = Mojolicious::Routes::Pattern->new('/');
+$pattern->defaults->{format}    = 'txt';
+$pattern->constraints->{format} = ['txt'];
+$result                         = $pattern->match('/', 1);
+is_deeply $result, {format => 'txt'}, 'right structure';
+
 # Unicode
 $pattern = Mojolicious::Routes::Pattern->new('/(one)♥(two)');
 $result  = $pattern->match('/i♥mojolicious');
@@ -31,17 +31,18 @@ my $t = Test::Mojo->new;
 
 # Simple POD template
 $t->get_ok('/')->status_is(200)
-  ->content_like(qr|<h1>Test123</h1>\s+<p>It <code>works</code>!</p>|);
+  ->content_like(qr!<h1 id="Test123">Test123</h1>!)
+  ->content_like(qr|<p>It <code>works</code>!</p>|);
 
 # POD helper
-$t->post_ok('/')->status_is(200)
-  ->content_like(qr!test123\s+<h1>A</h1>\s+<h1>B</h1>!)
+$t->post_ok('/')->status_is(200)->content_like(qr!test123<h1 id="A">A</h1>!)
+  ->content_like(qr!<h1 id="B">B</h1>!)
   ->content_like(qr!\s+<p><code>test</code></p>!)->content_like(qr/Gray/);
 
 # POD filter
 $t->post_ok('/block')->status_is(200)
-  ->content_like(qr!test321\s+<h2>lalala</h2>\s+<p><code>test</code></p>!)
-  ->content_like(qr/Gray/);
+  ->content_like(qr!test321<h2 id="lalala">lalala</h2>!)
+  ->content_like(qr!<p><code>test</code></p>!)->content_like(qr/Gray/);
 
 # Empty
 $t->get_ok('/empty')->status_is(200)->content_is('');
@@ -35,6 +35,10 @@ is $t->app->static->file('hello.txt')->slurp,
   "Hello Mojo from a static file!\n", 'right content';
 is $t->app->moniker, 'mojolicious_test', 'right moniker';
 
+# Default namespaces
+is_deeply $t->app->routes->namespaces,
+  ['MojoliciousTest::Controller', 'MojoliciousTest'], 'right namespaces';
+
 # Plugin::Test::SomePlugin2::register (security violation)
 $t->get_ok('/plugin-test-some_plugin2/register')->status_isnt(500)
   ->status_is(404)->header_is(Server => 'Mojolicious (Perl)')
@@ -1,6 +1,7 @@
 use Mojo::Base -strict;
 
 use Test::More;
+use Mojo::Util 'decode';
 use Mojolicious::Controller;
 
 # Partial rendering
@@ -61,4 +62,55 @@ $c->cookie(foo => 'x' x 4097);
 like $log, qr/Cookie "foo" is bigger than 4096 bytes\./, 'right message';
 $c->app->log->unsubscribe(message => $cb);
 
+# Nested helpers
+my $first = Mojolicious::Controller->new;
+$first->helpers->app->log->level('fatal');
+$first->app->helper('myapp.multi_level.test' => sub {'works!'});
+ok $first->app->renderer->get_helper('myapp'),                  'found helper';
+ok $first->app->renderer->get_helper('myapp.multi_level'),      'found helper';
+ok $first->app->renderer->get_helper('myapp.multi_level.test'), 'found helper';
+is $first->myapp->multi_level->test, 'works!', 'right result';
+is $first->helpers->myapp->multi_level->test, 'works!', 'right result';
+$first->app->helper('myapp.defaults' => sub { shift->app->defaults(@_) });
+ok $first->app->renderer->get_helper('myapp.defaults'), 'found helper';
+is $first->app->renderer->get_helper('myap.'),          undef, 'no helper';
+is $first->app->renderer->get_helper('yapp'),           undef, 'no helper';
+$first->myapp->defaults(foo => 'bar');
+is $first->myapp->defaults('foo'), 'bar', 'right result';
+is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';
+is $first->app->myapp->defaults('foo'),     'bar', 'right result';
+my $second = Mojolicious::Controller->new;
+$second->app->log->level('fatal');
+is $second->app->renderer->get_helper('myapp'),          undef, 'no helper';
+is $second->app->renderer->get_helper('myapp.defaults'), undef, 'no helper';
+$second->app->helper('myapp.defaults' => sub {'nothing'});
+my $myapp = $first->myapp;
+is $first->myapp->defaults('foo'),  'bar',     'right result';
+is $second->myapp->defaults('foo'), 'nothing', 'right result';
+is $second->helpers->myapp->defaults('foo'), 'nothing', 'right result';
+is $first->myapp->defaults('foo'), 'bar', 'right result';
+is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';
+
+# Missing method (AUTOLOAD)
+my $class = ref $first->myapp;
+eval { $first->myapp->missing };
+like $@, qr/^Can't locate object method "missing" via package "$class"/,
+  'right error';
+eval { $first->app->myapp->missing };
+like $@, qr/^Can't locate object method "missing" via package "$class"/,
+  'right error';
+
+# No leaky namespaces
+my $helper_class = ref $second->myapp;
+is ref $second->myapp, $helper_class, 'same class';
+ok $helper_class->can('defaults'), 'helpers are active';
+my $template_class = decode 'UTF-8',
+  $second->render_to_string(inline => "<%= __PACKAGE__ =%>");
+is decode('UTF-8', $second->render_to_string(inline => "<%= __PACKAGE__ =%>")),
+  $template_class, 'same class';
+ok $template_class->can('stash'), 'helpers are active';
+undef $second;
+ok !$helper_class->can('defaults'), 'helpers have been cleaned up';
+ok !$template_class->can('stash'),  'helpers have been cleaned up';
+
 done_testing();
@@ -194,6 +194,15 @@ $r->route('/missing/*/name')->to('missing#wildcard');
 $r->route('/missing/too/*', '' => ['test'])
   ->to('missing#too', '' => 'missing');
 
+# /partial/*
+$r->route('/partial')->detour('foo#bar');
+
+# GET  /similar/*
+# POST /similar/too
+my $similar = $r->bridge('/similar');
+$similar->route('/:something')->via('GET')->to('similar#get');
+$similar->route('/too')->via('POST')->to('similar#post');
+
 # Cached lookup
 my $fast = $r->route('/fast');
 is $r->find('fast'),   $fast, 'fast route found';
@@ -848,4 +857,27 @@ is_deeply $m->stack,
   'right structure';
 is $m->path_for->{path}, '/missing/too', 'right path';
 
+# Partial route
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/partial/test'});
+is_deeply $m->stack,
+  [{controller => 'foo', action => 'bar', 'path' => '/test'}],
+  'right structure';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/partial.test'});
+is_deeply $m->stack,
+  [{controller => 'foo', action => 'bar', 'path' => '.test'}],
+  'right structure';
+
+# Similar routes with placeholders
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/similar/too'});
+is_deeply $m->stack,
+  [{}, {controller => 'similar', action => 'get', 'something' => 'too'}],
+  'right structure';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'POST', path => '/similar/too'});
+is_deeply $m->stack, [{}, {controller => 'similar', action => 'post'}],
+  'right structure';
+
 done_testing();
@@ -12,14 +12,67 @@ use Test::Mojo;
 
 get '/hello3.txt' => sub { shift->render_static('hello2.txt') };
 
+post '/hello4.txt' => sub {
+  my $c = shift;
+  $c->res->headers->content_type('text/html');
+  $c->render_static('hello2.txt');
+};
+
+options '/hello.txt' => sub { shift->render(text => 'Options!') };
+
+get '/etag' => sub {
+  my $c = shift;
+  $c->is_fresh(etag => 'abc')
+    ? $c->rendered(304)
+    : $c->render(text => 'I ♥ Mojolicious!');
+};
+
 my $t = Test::Mojo->new;
 
+# Freshness
+my $c = $t->app->build_controller;
+ok !$c->is_fresh, 'content is stale';
+$c->res->headers->etag('"abc"');
+$c->req->headers->if_none_match('"abc"');
+ok $c->is_fresh, 'content is fresh';
+$c = $t->app->build_controller;
+my $date = Mojo::Date->new(23);
+$c->res->headers->last_modified($date);
+$c->req->headers->if_modified_since($date);
+ok $c->is_fresh, 'content is fresh';
+$c = $t->app->build_controller;
+$c->req->headers->if_none_match('"abc"');
+$c->req->headers->if_modified_since($date);
+ok $c->is_fresh(etag => 'abc', last_modified => $date->epoch),
+  'content is fresh';
+is $c->res->headers->etag,          '"abc"', 'right "ETag" value';
+is $c->res->headers->last_modified, "$date", 'right "Last-Modified" value';
+$c = $t->app->build_controller;
+ok !$c->is_fresh(last_modified => $date->epoch), 'content is stale';
+is $c->res->headers->etag,          undef,   'no "ETag" value';
+is $c->res->headers->last_modified, "$date", 'right "Last-Modified" value';
+
 # Static file
 $t->get_ok('/hello.txt')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 31)
   ->content_is("Hello Mojo from a static file!\n");
 
+# Static file (HEAD)
+$t->head_ok('/hello.txt')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 31)
+  ->content_is('');
+
+# Route for method other than GET and HEAD
+$t->options_ok('/hello.txt')->status_is(200)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Content-Length' => 8)->content_is('Options!');
+
+# Unknown method
+$t->put_ok('/hello.txt')->status_is(404)
+  ->header_is(Server => 'Mojolicious (Perl)');
+
 # Partial static file
 $t->get_ok('/hello.txt' => {Range => 'bytes=2-8'})->status_is(206)
   ->header_is(Server          => 'Mojolicious (Perl)')
@@ -80,6 +133,20 @@ $t->get_ok('/hello3.txt' => {Range => 'bytes=0-0'})->status_is(206)
   ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 1)
   ->header_is('Content-Range' => 'bytes 0-0/1')->content_is('X');
 
+# Render static file with custom content type
+$t->post_ok('/hello4.txt')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_type_is('text/html')
+  ->header_is('Content-Length' => 1)->content_is('X');
+
+# Fresh content
+$t->get_ok('/etag')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->header_is(ETag => '"abc"')->content_is('I ♥ Mojolicious!');
+
+# Stale content
+$t->get_ok('/etag' => {'If-None-Match' => '"abc"'})
+  ->header_is(Server => 'Mojolicious (Perl)')->header_is(ETag => '"abc"')
+  ->status_is(304)->content_is('');
+
 # Empty file
 $t->get_ok('/hello4.txt')->status_is(200)
   ->header_is(Server           => 'Mojolicious (Perl)')
@@ -87,13 +87,13 @@ $t->get_ok('/advanced')->status_is(200)->header_is('X-Append' => 'bar')
   ->content_is("&LT;escape me>\n123423");
 
 # Normal "pod" template
-$t->get_ok('/docs')->status_is(200)->content_like(qr!<h3>snowman</h3>!);
+$t->get_ok('/docs')->status_is(200)->content_like(qr!<h3.*>snowman</h3>!);
 
 # Template in "teapod" format
-$t->get_ok('/docs2')->status_is(200)->content_like(qr!<h2>snowman</h2>!);
+$t->get_ok('/docs2')->status_is(200)->content_like(qr!<h2.*>snowman</h2>!);
 
 # Empty stash value
-$t->get_ok('/docs3')->status_is(200)->content_like(qr!<h3></h3>!);
+$t->get_ok('/docs3')->status_is(200)->content_like(qr!<h3.*></h3>!);
 
 # REST request for "foo" format
 $t->get_ok('/rest')->status_is(200)->header_is('X-Rest' => 1)
@@ -132,6 +132,11 @@ $t->websocket_ok('/echo')->send_ok({binary => 'bytes!'})
   ->send_ok({binary => 'bytes!'})
   ->message_ok->message_isnt({text => 'bytes!'})->finish_ok;
 
+# Bytes in multiple frames
+$t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'a'])
+  ->send_ok([0, 0, 0, 0, 0, 'b'])->send_ok([1, 0, 0, 0, 0, 'c'])
+  ->message_ok->message_is({binary => 'abc'})->finish_ok;
+
 # Zero
 $t->websocket_ok('/echo')->send_ok(0)->message_ok->message_is('echo: 0')
   ->send_ok(0)->message_ok->message_like({text => qr/0/})->finish_ok(1000)