The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 22323
MANIFEST 57
META.json 58
META.yml 58
Makefile.PL 513
README.md 3929
examples/connect-proxy.pl 41
examples/entities.pl 51
examples/fast.pl 20
examples/hello-template.pl 010
examples/hello.pl 20
examples/microhttpd.pl 41
examples/websocket.pl 20
lib/Mojo/Asset/File.pm 1514
lib/Mojo/Asset/Memory.pm 011
lib/Mojo/Asset.pm 08
lib/Mojo/Base.pm 1621
lib/Mojo/ByteStream.pm 46
lib/Mojo/Cache.pm 25
lib/Mojo/Collection.pm 5998
lib/Mojo/Content/MultiPart.pm 1711
lib/Mojo/Content.pm 5051
lib/Mojo/Cookie.pm 11
lib/Mojo/DOM/CSS.pm 6543
lib/Mojo/DOM/HTML.pm 4125
lib/Mojo/DOM.pm 164242
lib/Mojo/Date.pm 1657
lib/Mojo/EventEmitter.pm 283
lib/Mojo/Exception.pm 11
lib/Mojo/Headers.pm 846
lib/Mojo/Home.pm 33
lib/Mojo/IOLoop/Client.pm 40135
lib/Mojo/IOLoop/Delay.pm 56
lib/Mojo/IOLoop/Server.pm 1720
lib/Mojo/IOLoop/Stream.pm 3021
lib/Mojo/IOLoop/certs/server.crt 021
lib/Mojo/IOLoop/certs/server.key 015
lib/Mojo/IOLoop/server.crt 210
lib/Mojo/IOLoop/server.key 150
lib/Mojo/IOLoop.pm 67
lib/Mojo/JSON/Pointer.pm 2123
lib/Mojo/JSON.pm 8675
lib/Mojo/Loader.pm 11
lib/Mojo/Log.pm 85
lib/Mojo/Message/Request.pm 2939
lib/Mojo/Message/Response.pm 67
lib/Mojo/Message.pm 4365
lib/Mojo/Parameters.pm 4256
lib/Mojo/Path.pm 1922
lib/Mojo/Reactor/EV.pm 99
lib/Mojo/Reactor/Poll.pm 35
lib/Mojo/Reactor.pm 103
lib/Mojo/Server/CGI.pm 43
lib/Mojo/Server/Daemon.pm 1213
lib/Mojo/Server/Hypnotoad.pm 1415
lib/Mojo/Server/Morbo.pm 1213
lib/Mojo/Server/PSGI.pm 32
lib/Mojo/Server/Prefork.pm 910
lib/Mojo/Server.pm 129
lib/Mojo/Template.pm 10693
lib/Mojo/Transaction/WebSocket.pm 5342
lib/Mojo/Transaction.pm 1523
lib/Mojo/URL.pm 921
lib/Mojo/UserAgent/CookieJar.pm 012
lib/Mojo/UserAgent/Proxy.pm 55
lib/Mojo/UserAgent/Server.pm 55
lib/Mojo/UserAgent/Transactor.pm 3070
lib/Mojo/UserAgent.pm 122129
lib/Mojo/Util.pm 262305
lib/Mojo/entities.txt 22310
lib/Mojo.pm 468
lib/Mojolicious/Command/cgi.pm 32
lib/Mojolicious/Command/cpanify.pm 11
lib/Mojolicious/Command/daemon.pm 01
lib/Mojolicious/Command/generate/app.pm 6361
lib/Mojolicious/Command/generate/lite_app.pm 3130
lib/Mojolicious/Command/generate/makefile.pm 1514
lib/Mojolicious/Command/generate/plugin.pm 5958
lib/Mojolicious/Command/get.pm 119
lib/Mojolicious/Command/inflate.pm 11
lib/Mojolicious/Command/prefork.pm 01
lib/Mojolicious/Command/routes.pm 96
lib/Mojolicious/Command/test.pm 208
lib/Mojolicious/Command/version.pm 1413
lib/Mojolicious/Command.pm 54
lib/Mojolicious/Commands.pm 1417
lib/Mojolicious/Controller.pm 168161
lib/Mojolicious/Guides/Contributing.pod 122
lib/Mojolicious/Guides/Cookbook.pod 50174
lib/Mojolicious/Guides/FAQ.pod 89
lib/Mojolicious/Guides/Growing.pod 72141
lib/Mojolicious/Guides/Rendering.pod 118237
lib/Mojolicious/Guides/Routing.pod 284256
lib/Mojolicious/Guides.pod 1023
lib/Mojolicious/Lite.pm 2949
lib/Mojolicious/Plugin/Config.pm 11
lib/Mojolicious/Plugin/DefaultHelpers.pm 31169
lib/Mojolicious/Plugin/EPLRenderer.pm 137
lib/Mojolicious/Plugin/EPRenderer.pm 2326
lib/Mojolicious/Plugin/JSONConfig.pm 106
lib/Mojolicious/Plugin/Mount.pm 31
lib/Mojolicious/Plugin/PODRenderer.pm 3823
lib/Mojolicious/Plugin/TagHelpers.pm 1716
lib/Mojolicious/Plugins.pm 1610
lib/Mojolicious/Renderer.pm 3759
lib/Mojolicious/Routes/Match.pm 3530
lib/Mojolicious/Routes/Pattern.pm 4845
lib/Mojolicious/Routes/Route.pm 8050
lib/Mojolicious/Routes.pm 2213
lib/Mojolicious/Static.pm 51102
lib/Mojolicious/Validator/Validation.pm 1535
lib/Mojolicious/templates/development.html.ep 99
lib/Mojolicious/templates/mojobar.html.ep 55
lib/Mojolicious/templates/perldoc.html.ep 411
lib/Mojolicious.pm 4460
lib/Test/Mojo.pm 4367
lib/ojo.pm 1428
script/hypnotoad 107
script/mojo 41
script/morbo 108
t/mojo/asset.t 1211
t/mojo/base.t 06
t/mojo/bytestream.t 1310
t/mojo/cache.t 29
t/mojo/certs/bad.crt 014
t/mojo/certs/bad.key 015
t/mojo/certs/badclient.crt 150
t/mojo/certs/badclient.key 150
t/mojo/certs/ca.crt 1111
t/mojo/certs/ca.key 1313
t/mojo/certs/client.crt 1010
t/mojo/certs/client.key 1313
t/mojo/certs/server.crt 1010
t/mojo/certs/server.key 1313
t/mojo/collection.t 6349
t/mojo/content.t 11
t/mojo/cookiejar.t 22
t/mojo/daemon.t 4261
t/mojo/date.t 035
t/mojo/delay.t 41
t/mojo/dom.t 215236
t/mojo/eventemitter.t 3111
t/mojo/headers.t 713
t/mojo/hypnotoad.t 41
t/mojo/ioloop.t 118
t/mojo/ioloop_ipv6.t 31
t/mojo/ioloop_tls.t 3126
t/mojo/json.t 6176
t/mojo/json_pointer.t 2224
t/mojo/lib/myapp.pl 05
t/mojo/morbo.t 41
t/mojo/parameters.t 1115
t/mojo/path.t 01
t/mojo/prefork.t 63
t/mojo/reactor_ev.t 1110
t/mojo/reactor_poll.t 1311
t/mojo/request.t 2071
t/mojo/response.t 1533
t/mojo/template.t 2251
t/mojo/transactor.t 1755
t/mojo/user_agent.t 1734
t/mojo/user_agent_online.t 65
t/mojo/user_agent_socks.t 0169
t/mojo/user_agent_tls.t 1512
t/mojo/util.t 624
t/mojo/websocket.t 1312
t/mojo/websocket_frames.t 215
t/mojo/websocket_proxy.t 1613
t/mojo/websocket_proxy_tls.t 2848
t/mojolicious/app.t 4476
t/mojolicious/charset_lite_app.t 41
t/mojolicious/commands.t 118
t/mojolicious/dispatch.t 45
t/mojolicious/dispatcher_lite_app.t 41
t/mojolicious/embedded_app.t 612
t/mojolicious/embedded_lite_app.t 2128
t/mojolicious/exception_lite_app.t 10
t/mojolicious/external/myapp.pl 111
t/mojolicious/external_app.t 10
t/mojolicious/external_lite_app.t 21
t/mojolicious/group_lite_app.t 1814
t/mojolicious/json_config_lite_app.t 36
t/mojolicious/json_config_mode_lite_app.t 10
t/mojolicious/layouted_lite_app.t 41
t/mojolicious/lib/SingleFileTestApp.pm 52
t/mojolicious/lite_app.t 4838
t/mojolicious/longpolling_lite_app.t 307
t/mojolicious/multipath_lite_app.t 41
t/mojolicious/ojo.t 114
t/mojolicious/pattern.t 318
t/mojolicious/pod_renderer_lite_app.t 3337
t/mojolicious/production_app.t 15
t/mojolicious/rebased_lite_app.t 41
t/mojolicious/renderer.t 057
t/mojolicious/restful_lite_app.t 41
t/mojolicious/routes.t 393
t/mojolicious/static_lite_app.t 6103
t/mojolicious/tag_helper_lite_app.t 65
t/mojolicious/testing_app.t 10
t/mojolicious/tls_lite_app.t 41
t/mojolicious/twinkle_lite_app.t 74
t/mojolicious/upload_lite_app.t 52
t/mojolicious/upload_stream_lite_app.t 41
t/mojolicious/validation_lite_app.t 46
t/mojolicious/websocket_lite_app.t 810
t/pod_coverage.t 24
206 files changed (This is a version diff) 64398279
@@ -1,4 +1,306 @@
 
+5.70  2014-12-18
+  - Improved Mojo::Template performance.
+  - Fixed error handling bugs in Mojo::IOLoop::Stream.
+  - Fixed a few limit bugs in Mojo::Message.
+  - Fixed Windows bug in Mojo::IOLoop::Client. (OlegG)
+
+5.69  2014-12-13
+  - Deprecated Mojo::DOM::siblings.
+  - Added following, following_siblings, preceding and preceding_siblings
+    methods to Mojo::DOM.
+  - Added port method to Mojo::IOLoop::Server.
+  - Removed deprecated emit_safe method from Mojo::EventEmitter.
+  - Removed deprecated render_static method from Mojolicious::Controller.
+  - Removed deprecated has_conditions method from Mojolicious::Routes::Route.
+  - Updated Net::DNS::Native requirement to 0.14 for some important bug fixes.
+  - Improved Mojo::DOM::HTML performance slightly.
+  - Fixed parent combinator bug in Mojo::DOM::CSS.
+  - Fixed whitespace bug in Mojo::DOM::HTML.
+  - Fixed Mojo::UserAgent::Transactor to handle query parameters more like
+    most common browsers.
+
+5.68  2014-12-02
+  - Improved Mojo::DOM::CSS performance significantly.
+  - Fixed deprecation warnings in get command.
+  - Fixed bug in Mojolicious::Controller where sending a WebSocket message
+    would cause multiple resume events.
+
+5.67  2014-11-27
+  - Improved overall performance by deserializing sessions only on demand.
+  - Fixed bug where embedded applications would deserialize sessions twice.
+
+5.66  2014-11-26
+  - Improved many WebSocket tests in Test::Mojo to be able to fail gracefully.
+  - Fixed bug in Mojo::DOM::CSS where the :empty pseudo class would not ignore
+    comments and processing instructions.
+
+5.65  2014-11-24
+  - Improved installable scripts to use #!perl. (jberger)
+  - Improved Mojo::JSON security by escaping the "/" character.
+  - Improved Mojolicious::Commands to reset the global Getopt::Long
+    configuration more safely.
+  - Fixed bug in Mojo::DOM::CSS where selected results would also include the
+    current root element.
+
+5.64  2014-11-22
+  - Fixed bug in Mojolicious::Commands where the global Getopt::Long
+    configuration would be changed after a command had already been loaded.
+
+5.63  2014-11-21
+  - Improved portability of some tests.
+  - Fixed a few multipart form handling bugs.
+
+5.62  2014-11-18
+  - Fixed bug in Mojolicious::Routes::Pattern where optional placeholders in
+    nested routes would sometimes not work correctly.
+  - Fixed bug where "handler" was not an allowed name for controller methods.
+
+5.61  2014-11-14
+  - Moved entities.txt into the DATA section of Mojo::Util to avoid
+    gratuitously breaking module bundlers.
+
+5.60  2014-11-11
+  - Added to_array method to Mojo::Collection.
+  - Added xss_escape function to Mojo::Util.
+  - Updated Net::DNS::Native requirement to 0.12 for some important bug fixes.
+
+5.59  2014-11-07
+  - Added support for non-blocking name resolution with Net::DNS::Native.
+
+5.58  2014-11-06
+  - Improved error handling in Mojo::IOLoop::Client.
+
+5.57  2014-11-02
+  - Deprecated stringification support in Mojo::Collection in favor of
+    Mojo::Collection::join.
+  - Deprecated Mojo::Collection::pluck in favor of Mojo::Collection::map.
+  - Deprecated Mojo::DOM::val.
+  - Improved map method in Mojo::Collection to be able to call methods.
+  - Improved tap method in Mojo::Base to be able to call methods.
+
+5.56  2014-10-29
+  - Deprecated Mojo::Collection::AUTOLOAD in favor of Mojo::Collection::pluck.
+  - Deprecated Mojo::DOM::AUTOLOAD in favor of Mojo::DOM::children.
+
+5.55  2014-10-28
+  - Deprecated support for data arguments in Mojo::JSON::Pointer.
+  - Added access_control_allow_origin, content_language, content_location and
+    strict_transport_security methods to Mojo::Headers.
+
+5.54  2014-10-23
+  - Deprecated object-oriented Mojo::JSON API.
+  - Added auto_decompress attribute to Mojo::Content.
+  - Improved Mojo::Content to parse content more defensively.
+  - Fixed chunked transfer encoding bug in Mojo::Content.
+  - Fixed bug where Mojo::UserAgent would try to follow redirects for
+    protocols other than HTTP and HTTPS.
+
+5.53  2014-10-20
+  - Fixed bug in Mojo::Server where secondary groups were not reassigned
+    correctly. (ksm, sri)
+
+5.52  2014-10-18
+  - Fixed read-only file system compatibility of Mojo::Asset::File.
+
+5.51  2014-10-17
+  - Fixed bug in Mojolicious::Validator::Validation where every_param would
+    sometimes return an array reference containing an undef value.
+  - Fixed Mojo::ByteStream and Mojo::Collection to always return true in
+    boolean context.
+
+5.50  2014-10-15
+  - Improved Mojo::DOM::HTML performance slightly.
+  - Fixed description list parsing bug in Mojo::DOM::HTML. (Trelane)
+
+5.49  2014-10-10
+  - Improved form content generator to allow custom content types.
+  - Improved Mojo::Server to load applications consistently for all servers.
+    (tianon, sri)
+  - Fixed Mojolicious::Static to hide files without extensions in DATA
+    sections.
+  - Fixed inflate command to ignore files without extensions.
+  - Fixed bug in Mojolicious::Routes::Route where formats could be rendered
+    twice for embedded applications.
+
+5.48  2014-10-07
+  - Emergency release for a serious security issue that can result in
+    parameter injection attacks, everybody should update!
+    Breaking change: Methods that previously worked differently in scalar than
+    in list context now always assume scalar context, and new methods have
+    been added to cover the list context functionality.
+  - Added every_cookie and every_upload methods to Mojo::Message.
+  - Added every_param method to Mojo::Message::Request.
+  - Added every_param method to Mojo::Parameters.
+  - Added every_cookie, every_param and every_signed_cookie methods to
+    Mojolicious::Controller.
+  - Added every_param method to Mojolicious::Validator::Validation.
+  - Added from_json and to_json functions to Mojo::JSON.
+  - Improved pluck method in Mojo::Collection to be able to extract values
+    from hash references.
+
+5.47  2014-09-28
+  - Improved url_for performance.
+
+5.46  2014-09-26
+  - PAUSE lost the previous release.
+
+5.45  2014-09-26
+  - Deprecated Mojolicious::Routes::Route::has_conditions.
+  - Added extracting attribute to Mojo::UserAgent::CookieJar.
+  - Improved performance of next, next_sibling, previous and previous_sibling
+    methods in Mojo::DOM significantly.
+  - Improved Mojo::Cache to allow caching to be disabled. (mvgrimes, sri)
+  - Fixed url_for bug where deeply nested WebSocket routes would not work
+    correctly.
+
+5.44  2014-09-23
+  - Fixed bug in Mojolicious::Renderer that prevented proxy objects from being
+    reused.
+
+5.43  2014-09-22
+  - Updated Makefile.PL for version 2 of the CPAN distribution metadata
+    specification.
+  - Improved get command to not depend on Content-Type headers for
+    differentiating between JSON and HTML/XML.
+
+5.42  2014-09-17
+  - Fixed url_for bug where an unnecessary slash could be rendered before
+    formats.
+
+5.41  2014-09-13
+  - Deprecated Mojolicious::Controller::render_static in favor of
+    reply->static helper.
+  - Added mtime attribute to Mojo::Asset::Memory.
+  - Added mtime method to Mojo::Asset and Mojo::Asset::File.
+  - Added reply->asset and reply->static helpers to
+    Mojolicious::Plugin::DefaultHelpers.
+  - Fixed bug in Mojo::UserAgent where connections would sometimes not get
+    closed correctly.
+
+5.40  2014-09-12
+  - Deprecated Mojo::EventEmitter::emit_safe.
+  - Added reply->exception and reply->not_found helpers to
+    Mojolicious::Plugin::DefaultHelpers.
+  - Improved all events to handle exceptions the same.
+
+5.39  2014-09-07
+  - Improved decamelize performance.
+  - Fixed bug in Mojo::Template where newline characters could get lost.
+
+5.38  2014-09-05
+  - Improved routes command to use new terminology for flags.
+  - Fixed bug in Mojo::Util where tablify could not handle empty columns.
+
+5.37  2014-09-03
+  - Improved Mojo::Template performance slightly.
+  - Fixed .ep template bug where the stash value "c" could no longer be used.
+
+5.36  2014-09-02
+  - Improved Mojo::Template performance.
+
+5.35  2014-08-30
+  - Improved monkey_patch to be able to name generated functions.
+
+5.34  2014-08-29
+  - Added original_remote_address attribute to Mojo::Transaction.
+  - Fixed bug where Mojolicious::Commands would change @ARGV when loaded.
+
+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 +393,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 +431,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
@@ -312,7 +613,7 @@
   - Improved router to allow format detection for bridges.
 
 4.68  2014-01-22
-  - Added Mojo::DOM::Node.
+  - Added module Mojo::DOM::Node.
   - Added contents and node methods to Mojo::DOM.
   - Removed deprecated http_proxy, https_proxy, name and no_proxy attributes
     from Mojo::UserAgent.
@@ -1384,7 +1685,7 @@
 
 2.93  2012-05-05
   - Added remove method to Mojolicious::Routes::Route.
-  - Improved 32bit Perl support of Mojo::Transaction::WebSocket.
+  - Improved 32-bit Perl support of Mojo::Transaction::WebSocket.
     (mikemagowan, sri)
   - Improved exception handling of application and configuration file loaders.
   - Improved exception handling of Mojolicious::Plugin::I18N.
@@ -1539,7 +1840,7 @@
 2.65  2012-03-22
   - Deprecated Mojo::IOLoop::drop in favor of Mojo::IOLoop::remove.
   - Renamed Mojo::Reactor::drop to Mojo::Reactor::remove.
-  - Added Mojo::Reactor::Poll.
+  - Added module Mojo::Reactor::Poll.
   - Added one_tick method to Mojo::Reactor and Mojo::Reactor::EV.
   - Removed experimental status from Mojo::IOLoop::Client.
   - Removed experimental status from Mojo::IOLoop::Server.
@@ -1559,7 +1860,7 @@
 
 2.63  2012-03-20
   - Renamed Mojo::IOWatcher to Mojo::Reactor.
-  - Added Mojolicious::Routes::Route.
+  - Added module Mojolicious::Routes::Route.
   - Added find and root methods to Mojolicious::Routes::Route.
   - Improved form_for helper to automatically add method="POST" attributes
     when necessary.
@@ -2323,7 +2624,7 @@
   - Modernized Mojo::HelloWorld.
   - Improved HTML healing capabilities of Mojo::DOM::HTML.
   - Improved HTML rendering in Mojo::DOM::HTML.
-  - Improved 64bit support in Mojo::Transaction::WebSocket.
+  - Improved 64-bit support in Mojo::Transaction::WebSocket.
   - Fixed memory leak in Mojo::IOLoop::Client.
   - Fixed memory leak in Mojolicious.
   - Fixed small bug in Mojo::IOLoop::Server.
@@ -2568,7 +2869,7 @@
     available.
   - Updated jQuery to version 1.6.
   - Fixed PSGI read error handling.
-  - Fixed 64bit WebSocket message bug.
+  - Fixed 64-bit WebSocket message bug.
   - Fixed small Windows bug.
 
 1.22  2011-05-02
@@ -2601,7 +2902,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,10 +3432,10 @@
       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.
+    means "plackup myapp.pl" and "plackup script/my_app" should just work.
   - Added session and flash helpers.
   - Added finished callback to WebSocket client and server.
   - Added referrer method to Mojo::Headers. (esskar)
@@ -3158,14 +3459,14 @@
   - 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)
   - Added automatic CGI/FastCGI environment detection to Mojo::Commands.
   - Renamed Mojolicious::Book to Mojolicious::Guides.
-  - Removed hot deployment support for Windows because of incompatibilities
+  - Removed hot deployment support for Windows, because of incompatibilities
     between Active Perl and Strawberry Perl.
   - Made process id and lock file defaults more user-friendly in
     Mojo::Server::Daemon.
@@ -3440,8 +3741,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
@@ -3643,7 +3944,7 @@
   - Added layout support to MojoX::Renderer.
   - Made render call optional.
   - Added format support to MojoX::Routes.
-  - Added Mojo::Loader::Exception.
+  - Added module Mojo::Loader::Exception.
   - Added wildcard symbol support to MojoX::Routes and rewrote many routes
     internals.
   - Added Makefile.PL generator.
@@ -3654,7 +3955,7 @@
   - Added encoding support to Mojo::Template and made "utf8" the default.
   - Added HEAD support to Mojo::Server::Daemon. (acajou)
   - Added new relaxed placeholder to MojoX::Routes::Pattern.
-  - Added Mojo::Template::Exception.
+  - Added module Mojo::Template::Exception.
   - Added HEAD support to the Mojo::Transaction state machine and related
     modules. (acajou)
   - Added safe_post option to Mojo::Pipeline. (acajou)
@@ -3695,10 +3996,10 @@
 
 0.9  2008-12-01
   - Added modes to Mojolicious.
-  - Added Mojo::Log and log support for Mojo/Mojolicious.
+  - Added module Mojo::Log and log support for Mojo/Mojolicious.
+  - Added module MojoX::Context.
   - Changed MojoX::Renderer and Mojo::Template API to make catching errors
     easier, we now use a scalar ref for results like most template engines.
-  - Added MojoX::Context.
   - Added multi-level controller class support to Mojolicious.
   - MojoX::Dispatcher::Routes should be able to fail.
   - Added diagnostics functions to Mojo::HelloWorld.
@@ -3817,7 +4118,7 @@
   - Added workaround for missing IO::Seekable support in older versions of
     File::Temp (Perl 5.8).
   - script/mojo.pl got renamed to bin/mojo.
-  - Mojo::Cache got renamed to Mojo::File because there will be a cache
+  - Mojo::Cache got renamed to Mojo::File, because there will be a cache
     module named MojoX::Cache, and that could cause confusion later on.
   - Fixed many escaping related bugs around Mojo::URL.
   - Fixed 100-Continue support in Mojo::Server::Daemon and Mojo::Client.
@@ -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
@@ -25,17 +26,16 @@ lib/Mojo/Date.pm
 lib/Mojo/DOM.pm
 lib/Mojo/DOM/CSS.pm
 lib/Mojo/DOM/HTML.pm
-lib/Mojo/entities.txt
 lib/Mojo/EventEmitter.pm
 lib/Mojo/Exception.pm
 lib/Mojo/Headers.pm
 lib/Mojo/HelloWorld.pm
 lib/Mojo/Home.pm
 lib/Mojo/IOLoop.pm
+lib/Mojo/IOLoop/certs/server.crt
+lib/Mojo/IOLoop/certs/server.key
 lib/Mojo/IOLoop/Client.pm
 lib/Mojo/IOLoop/Delay.pm
-lib/Mojo/IOLoop/server.crt
-lib/Mojo/IOLoop/server.key
 lib/Mojo/IOLoop/Server.pm
 lib/Mojo/IOLoop/Stream.pm
 lib/Mojo/JSON.pm
@@ -181,8 +181,8 @@ t/mojo/asset.t
 t/mojo/base.t
 t/mojo/bytestream.t
 t/mojo/cache.t
-t/mojo/certs/badclient.crt
-t/mojo/certs/badclient.key
+t/mojo/certs/bad.crt
+t/mojo/certs/bad.key
 t/mojo/certs/ca.crt
 t/mojo/certs/ca.key
 t/mojo/certs/client.crt
@@ -216,6 +216,7 @@ t/mojo/lib/Mojo/LoaderException2.pm
 t/mojo/lib/Mojo/LoaderTest/A.pm
 t/mojo/lib/Mojo/LoaderTest/B.pm
 t/mojo/lib/Mojo/LoaderTest/C.pm
+t/mojo/lib/myapp.pl
 t/mojo/loader.t
 t/mojo/log.t
 t/mojo/morbo.t
@@ -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 7.04, CPAN::Meta::Converter version 2.143240",
    "license" : [
       "artistic_2"
    ],
@@ -33,6 +33,9 @@
       },
       "runtime" : {
          "requires" : {
+            "IO::Socket::IP" : "0.26",
+            "Pod::Simple" : "3.09",
+            "Time::Local" : "1.2",
             "perl" : "5.010001"
          }
       }
@@ -40,16 +43,16 @@
    "release_status" : "stable",
    "resources" : {
       "bugtracker" : {
-         "web" : "http://github.com/kraih/mojo/issues"
+         "web" : "https://github.com/kraih/mojo/issues"
       },
       "homepage" : "http://mojolicio.us",
       "license" : [
          "http://www.opensource.org/licenses/artistic-license-2.0"
       ],
       "repository" : {
-         "url" : "http://github.com/kraih/mojo"
+         "url" : "https://github.com/kraih/mojo.git"
       },
-      "x_MailingList" : "http://groups.google.com/group/mojolicious"
+      "x_IRC" : "irc://irc.perl.org/#mojo"
    },
-   "version" : "5.16"
+   "version" : "5.70"
 }
@@ -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 7.04, CPAN::Meta::Converter version 2.143240'
 license: artistic_2
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -19,11 +19,14 @@ no_index:
     - inc
     - t
 requires:
+  IO::Socket::IP: '0.26'
+  Pod::Simple: '3.09'
+  Time::Local: '1.2'
   perl: '5.010001'
 resources:
-  MailingList: http://groups.google.com/group/mojolicious
-  bugtracker: http://github.com/kraih/mojo/issues
+  IRC: irc://irc.perl.org/#mojo
+  bugtracker: https://github.com/kraih/mojo/issues
   homepage: http://mojolicio.us
   license: http://www.opensource.org/licenses/artistic-license-2.0
-  repository: http://github.com/kraih/mojo
-version: '5.16'
+  repository: https://github.com/kraih/mojo.git
+version: '5.70'
@@ -5,6 +5,9 @@ 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
+# IO::Socket::IP 0.26 first shipped with Perl 5.19.8
 WriteMakefile(
   NAME         => 'Mojolicious',
   VERSION_FROM => 'lib/Mojolicious.pm',
@@ -14,14 +17,19 @@ WriteMakefile(
   META_MERGE   => {
     requires  => {perl => '5.010001'},
     resources => {
-      homepage    => 'http://mojolicio.us',
-      license     => 'http://www.opensource.org/licenses/artistic-license-2.0',
-      MailingList => 'http://groups.google.com/group/mojolicious',
-      repository  => 'http://github.com/kraih/mojo',
-      bugtracker  => 'http://github.com/kraih/mojo/issues'
+      license    => 'http://www.opensource.org/licenses/artistic-license-2.0',
+      homepage   => 'http://mojolicio.us',
+      bugtracker => 'https://github.com/kraih/mojo/issues',
+      repository => 'https://github.com/kraih/mojo.git',
+      x_IRC      => 'irc://irc.perl.org/#mojo'
     },
     no_index => {directory => ['t']}
   },
+  PREREQ_PM => {
+    'IO::Socket::IP' => '0.26',
+    'Pod::Simple'    => '3.09',
+    'Time::Local'    => '1.2'
+  },
   EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'],
   test => {TESTS => 't/*.t t/*/*.t'}
 );
@@ -2,7 +2,7 @@
 # Mojolicious [![Build Status](https://travis-ci.org/kraih/mojo.svg?branch=master)](https://travis-ci.org/kraih/mojo)
 
   Back in the early days of the web, many people learned Perl because of a
-  wonderful Perl library called [CGI](http://metacpan.org/module/CGI). It was
+  wonderful Perl library called [CGI](https://metacpan.org/module/CGI). It was
   simple enough to get started without knowing much about the language and
   powerful enough to keep you going, learning by doing was much fun. While
   most of the techniques used are outdated now, the idea behind it is not.
@@ -12,19 +12,17 @@
 ## Features
 
   * An amazing real-time web framework, allowing you to easily grow single
-    file [Mojolicious::Lite](http://mojolicio.us/perldoc/Mojolicious/Lite)
-    prototypes into well structured web applications.
+    file prototypes into well-structured web applications.
     * Powerful out of the box with RESTful routes, plugins, commands, Perl-ish
       templates, content negotiation, session management, form validation,
       testing framework, static file server, first class Unicode support and
       much more for you to discover.
-  * Very clean, portable and Object Oriented pure-Perl API without any hidden
-    magic and no requirements besides Perl 5.10.1 (although 5.18+ is
-    recommended, and optional CPAN modules will be used to provide advanced
-    functionality if they are installed).
+  * Very clean, portable and object-oriented pure-Perl API with no hidden
+    magic and no requirements besides Perl 5.20.0 (versions as old as 5.10.1
+    can be used too, but may require additional CPAN modules to be 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.
@@ -36,7 +34,7 @@
 
   All you need is a one-liner, it takes less than a minute.
 
-    $ curl get.mojolicio.us | sh
+    $ curl -L https://cpanmin.us | perl - -M https://cpan.metacpan.org -n Mojolicious
 
   We recommend the use of a [Perlbrew](http://perlbrew.pl) environment.
 
@@ -47,7 +45,7 @@
 ```perl
 use Mojolicious::Lite;
 
-get '/' => {text => 'Hello World!'};
+get '/' => {text => 'I ♥ Mojolicious!'};
 
 app->start;
 ```
@@ -59,50 +57,42 @@ app->start;
     Server available at http://127.0.0.1:3000.
 
     $ curl http://127.0.0.1:3000/
-    Hello World!
+    I ♥ Mojolicious!
 
 ## Duct tape for the HTML5 web
 
-  Web development for humans, making hard things possible and everything fun.
+  Use all the latest Perl and HTML features in beautiful single file
+  prototypes like this one, and grow them easily into well-structured
+  applications.
 
 ```perl
 use Mojolicious::Lite;
+use 5.20.0;
+use experimental 'signatures';
 
-# Simple plain text response
-get '/' => {text => 'I ♥ Mojolicious!'};
-
-# Route associating "/time" with template in DATA section
-get '/time' => 'clock';
+# Render template "index.html.ep" from the DATA section
+get '/' => {template => 'index'};
 
-# Scrape information from remote sites
-post '/title' => sub {
-  my $c     = shift;
-  my $url   = $c->param('url') || 'http://mojolicio.us';
-  my $title = $c->ua->get($url)->res->dom->at('title')->text;
-  $c->render(json => {url => $url, title => $title});
-};
-
-# WebSocket echo service
-websocket '/echo' => sub {
-  my $c = shift;
-  $c->on(message => sub {
-    my ($c, $msg) = @_;
-    $c->send("echo: $msg");
+# WebSocket service used by the template to extract the title from a web site
+websocket '/title' => sub ($c) {
+  $c->on(message => sub ($c, $msg) {
+    my $title = $c->ua->get($msg)->res->dom->at('title')->text;
+    $c->send($title);
   });
 };
 
 app->start;
 __DATA__
 
-@@ clock.html.ep
-% use Time::Piece;
-% my $now = localtime;
-The time is <%= $now->hms %>.
+@@ index.html.ep
+% my $url = url_for 'title';
+<script>
+  var ws = new WebSocket('<%= $url->to_abs %>');
+  ws.onmessage = function (event) { document.body.innerHTML += event.data };
+  ws.onopen    = function (event) { ws.send('http://mojolicio.us') };
+</script>
 ```
 
-  Single file prototypes like this one can easily grow into well-structured
-  applications.
-
 ## Want to know more?
 
   Take a look at our excellent [documentation](http://mojolicio.us/perldoc>)!
@@ -1,7 +1,4 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::IOLoop;
 
 # Minimal CONNECT proxy server to test TLS tunneling
@@ -76,7 +73,7 @@ Mojo::IOLoop->server(
       }
     );
   }
-) or die "Couldn't create listen socket!\n";
+);
 
 print <<'EOF';
 Starting CONNECT proxy on port 3000.
@@ -1,13 +1,9 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::ByteStream 'b';
 use Mojo::UserAgent;
 
 # Extract named character references from HTML spec
-my $tx = Mojo::UserAgent->new->get(
-  'http://www.whatwg.org/specs/web-apps/current-work/');
+my $tx = Mojo::UserAgent->new->get('https://html.spec.whatwg.org');
 b($_->at('td > code')->text . ' ' . $_->children('td')->[1]->text)->trim->say
   for $tx->res->dom('#named-character-references-table tbody > tr')->each;
 
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base 'Mojolicious';
 
 sub handler {
@@ -0,0 +1,10 @@
+use Mojolicious::Lite;
+
+get '/hello';
+
+# Minimal "Hello World" application with template for profiling
+app->start;
+__DATA__
+
+@@ hello.html.ep
+Hello World!
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojolicious::Lite;
 
 get '/' => {data => 'Hello World!'};
@@ -1,7 +1,4 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::IOLoop;
 
 # Minimal ioloop example demonstrating how to cheat at HTTP benchmarks :)
@@ -32,7 +29,7 @@ Mojo::IOLoop->server(
     );
     $stream->on(close => sub { delete $buffer{$id} });
   }
-) or die "Couldn't create listen socket!\n";
+);
 
 print <<'EOF';
 Starting server on port 8080.
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojolicious::Lite;
 
 websocket '/test' => sub {
@@ -17,8 +17,7 @@ has handle => sub {
   my $handle = IO::File->new;
   my $path   = $self->path;
   if (defined $path && -f $path) {
-    $handle->open($path, -w _ ? O_APPEND | O_RDWR : O_RDONLY)
-      or croak qq{Can't open file "$path": $!};
+    $handle->open($path, O_RDONLY) or croak qq{Can't open file "$path": $!};
     return $handle;
   }
 
@@ -78,8 +77,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 +96,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) }
@@ -122,17 +119,13 @@ sub move_to {
   return $self->path($to)->cleanup(0);
 }
 
-sub size {
-  return 0 unless defined(my $file = shift->path);
-  return -s $file;
-}
+sub mtime { (stat shift->handle)[9] }
+
+sub size { -s shift->handle }
 
 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;
@@ -238,6 +231,12 @@ True.
 
 Move asset data into a specific file and disable L</"cleanup">.
 
+=head2 mtime
+
+  my $mtime = $file->mtime;
+
+Modification time of asset.
+
 =head2 size
 
   my $size = $file->size;
@@ -4,8 +4,12 @@ use Mojo::Base 'Mojo::Asset';
 use Mojo::Asset::File;
 use Mojo::Util 'spurt';
 
+# Last modified default
+my $MTIME = time;
+
 has 'auto_upgrade';
 has max_memory_size => sub { $ENV{MOJO_MAX_MEMORY_SIZE} || 262144 };
+has mtime => sub {$MTIME};
 
 sub add_chunk {
   my ($self, $chunk) = @_;
@@ -114,6 +118,13 @@ Maximum size in bytes of data to keep in memory before automatically upgrading
 to a L<Mojo::Asset::File> object, defaults to the value of the
 C<MOJO_MAX_MEMORY_SIZE> environment variable or C<262144> (256KB).
 
+=head2 mtime
+
+  my $mtime = $mem->mtime;
+  $mem      = $mem->mtime(1408567500);
+
+Modification time of asset, defaults to the time this class was loaded.
+
 =head1 METHODS
 
 L<Mojo::Asset::Memory> inherits all methods from L<Mojo::Asset> and implements
@@ -15,6 +15,7 @@ sub is_file {undef}
 sub is_range { !!($_[0]->end_range || $_[0]->start_range) }
 
 sub move_to { croak 'Method "move_to" not implemented by subclass' }
+sub mtime   { croak 'Method "mtime" not implemented by subclass' }
 sub size    { croak 'Method "size" not implemented by subclass' }
 sub slurp   { croak 'Method "slurp" not implemented by subclass' }
 
@@ -35,6 +36,7 @@ Mojo::Asset - HTTP content storage base class
   sub contains  {...}
   sub get_chunk {...}
   sub move_to   {...}
+  sub mtime     {...}
   sub size      {...}
   sub slurp     {...}
 
@@ -108,6 +110,12 @@ Check if asset has a L</"start_range"> or L</"end_range">.
 
 Move asset data into a specific file. Meant to be overloaded in a subclass.
 
+=head2 mtime
+
+  my $mtime = $asset->mtime;
+
+Modification time of asset. Meant to be overloaded in a subclass.
+
 =head2 size
 
   my $size = $asset->size;
@@ -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";
@@ -87,8 +84,8 @@ sub new {
 }
 
 sub tap {
-  my ($self, $cb) = @_;
-  $_->$cb for $self;
+  my ($self, $cb) = (shift, shift);
+  $_->$cb(@_) for $self;
   return $self;
 }
 
@@ -186,12 +183,12 @@ L<Mojo::Base> implements the following methods.
 =head2 attr
 
   $object->attr('name');
-  BaseSubClass->attr('name');
-  BaseSubClass->attr([qw(name1 name2 name3)]);
-  BaseSubClass->attr(name => 'foo');
-  BaseSubClass->attr(name => sub {...});
-  BaseSubClass->attr([qw(name1 name2 name3)] => 'foo');
-  BaseSubClass->attr([qw(name1 name2 name3)] => sub {...});
+  SubClass->attr('name');
+  SubClass->attr([qw(name1 name2 name3)]);
+  SubClass->attr(name => 'foo');
+  SubClass->attr(name => sub {...});
+  SubClass->attr([qw(name1 name2 name3)] => 'foo');
+  SubClass->attr([qw(name1 name2 name3)] => sub {...});
 
 Create attribute accessor for hash-based objects, an array reference can be
 used to create more than one at a time. Pass an optional second argument to
@@ -202,9 +199,9 @@ argument.
 
 =head2 new
 
-  my $object = BaseSubClass->new;
-  my $object = BaseSubClass->new(name => 'value');
-  my $object = BaseSubClass->new({name => 'value'});
+  my $object = SubClass->new;
+  my $object = SubClass->new(name => 'value');
+  my $object = SubClass->new({name => 'value'});
 
 This base class provides a basic constructor for hash-based objects. You can
 pass it either a hash or a hash reference with attribute values.
@@ -212,11 +209,19 @@ pass it either a hash or a hash reference with attribute values.
 =head2 tap
 
   $object = $object->tap(sub {...});
+  $object = $object->tap($method);
+  $object = $object->tap($method, @args);
 
 K combinator, tap into a method chain to perform operations on an object
 within the chain. The object will be the first argument passed to the callback
 and is also available as C<$_>.
 
+  # Longer version
+  $object = $object->tap(sub { $_->$method(@args) });
+
+  # Inject side effects into a method chain
+  $object->foo('A')->tap(sub { say $_->foo })->foo('B');
+
 =head1 DEBUGGING
 
 You can set the C<MOJO_BASE_DEBUG> environment variable to get some advanced
@@ -1,6 +1,6 @@
 package Mojo::ByteStream;
 use Mojo::Base -strict;
-use overload '""' => sub { shift->to_string }, fallback => 1;
+use overload bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
 
 use Exporter 'import';
 use Mojo::Collection;
@@ -11,8 +11,8 @@ our @EXPORT_OK = ('b');
 # Turn most functions from Mojo::Util into methods
 my @UTILS = (
   qw(b64_decode b64_encode camelize decamelize hmac_sha1_sum html_unescape),
-  qw(md5_bytes md5_sum punycode_decode punycode_encode quote secure_compare),
-  qw(sha1_bytes sha1_sum slurp spurt squish trim unindent unquote url_escape),
+  qw(md5_bytes md5_sum punycode_decode punycode_encode quote sha1_bytes),
+  qw(sha1_sum slurp spurt squish trim unindent unquote url_escape),
   qw(url_unescape xml_escape xor_encode)
 );
 for my $name (@UTILS) {
@@ -43,6 +43,8 @@ sub say {
   return $self;
 }
 
+sub secure_compare { Mojo::Util::secure_compare ${shift()}, shift }
+
 sub size { length ${$_[0]} }
 
 sub split {
@@ -355,7 +357,7 @@ Always true.
 
   my $str = "$bytestream";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -8,9 +8,11 @@ sub get { (shift->{cache} || {})->{shift()} }
 sub set {
   my ($self, $key, $value) = @_;
 
+  return $self unless (my $max = $self->max_keys) > 0;
+
   my $cache = $self->{cache} ||= {};
   my $queue = $self->{queue} ||= [];
-  delete $cache->{shift @$queue} while @$queue >= $self->max_keys;
+  delete $cache->{shift @$queue} while @$queue >= $max;
   push @$queue, $key unless exists $cache->{$key};
   $cache->{$key} = $value;
 
@@ -46,7 +48,8 @@ L<Mojo::Cache> implements the following attributes.
   my $max = $cache->max_keys;
   $cache  = $cache->max_keys(50);
 
-Maximum number of cache keys, defaults to C<100>.
+Maximum number of cache keys, defaults to C<100>. Setting the value to C<0>
+will disable caching.
 
 =head1 METHODS
 
@@ -1,32 +1,41 @@
 package Mojo::Collection;
 use Mojo::Base -strict;
-use overload
-  bool     => sub { !!@{shift()} },
-  '""'     => sub { shift->join("\n") },
-  fallback => 1;
 
 use Carp 'croak';
 use Exporter 'import';
 use List::Util;
 use Mojo::ByteStream;
+use Mojo::Util 'deprecated';
 use Scalar::Util 'blessed';
 
+# DEPRECATED in Tiger Face!
+use overload '""' => sub {
+  deprecated 'Stringification support in Mojo::Collection is DEPRECATED'
+    . ' in favor of Mojo::Collection::join';
+  shift->join("\n");
+};
+use overload bool => sub {1}, fallback => 1;
+
 our @EXPORT_OK = ('c');
 
+# DEPRECATED in Tiger Face!
 sub AUTOLOAD {
   my $self = shift;
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  deprecated "Mojo::Collection::AUTOLOAD ($method) is DEPRECATED"
+    . ' in favor of Mojo::Collection::map';
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
-  return $self->pluck($method, @_);
+  return $self->map($method, @_);
 }
 
+# DEPRECATED in Tiger Face!
 sub DESTROY { }
 
 sub c { __PACKAGE__->new(@_) }
 
 sub compact {
-  shift->grep(sub { length($_ // '') });
+  $_[0]->new(grep { defined && (ref || length) } @{$_[0]});
 }
 
 sub each {
@@ -59,8 +68,8 @@ sub join {
 sub last { shift->[-1] }
 
 sub map {
-  my ($self, $cb) = @_;
-  return $self->new(map { $_->$cb } @$self);
+  my ($self, $cb) = (shift, shift);
+  return $self->new(map { $_->$cb(@_) } @$self);
 }
 
 sub new {
@@ -68,9 +77,18 @@ sub new {
   return bless [@_], ref $class || $class;
 }
 
+# DEPRECATED in Tiger Face!
 sub pluck {
-  my ($self, $method, @args) = @_;
-  return $self->map(sub { $_->$method(@args) });
+  deprecated
+    'Mojo::Collection::pluck is DEPRECATED in favor of Mojo::Collection::map';
+  my ($self, $key) = (shift, shift);
+  return $self->new(map { ref eq 'HASH' ? $_->{$key} : $_->$key(@_) } @$self);
+}
+
+sub reduce {
+  my $self = shift;
+  @_ = (@_, @$self);
+  goto &List::Util::reduce;
 }
 
 sub reverse { $_[0]->new(reverse @{$_[0]}) }
@@ -86,14 +104,25 @@ 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 to_array { [@{shift()}] }
+
 sub uniq {
   my %seen;
-  return shift->grep(sub { !$seen{$_}++ });
+  return $_[0]->new(grep { !$seen{$_}++ } @{$_[0]});
 }
 
 sub _flatten {
@@ -117,17 +146,14 @@ Mojo::Collection - Collection
   # Manipulate collection
   my $collection = Mojo::Collection->new(qw(just works));
   unshift @$collection, 'it';
+  say $collection->join("\n");
 
   # Chain methods
   $collection->map(sub { ucfirst })->shuffle->each(sub {
-    my ($word, $count) = @_;
-    say "$count: $word";
+    my ($word, $num) = @_;
+    say "$num: $word";
   });
 
-  # Stringify collection
-  say $collection->join("\n");
-  say "$collection";
-
   # Use the alternative constructor
   use Mojo::Collection 'c';
   c(qw(a b c))->join('/')->url_escape->say;
@@ -163,6 +189,9 @@ L<Mojo::Collection> implements the following methods.
 Create a new collection with all elements that are defined and not an empty
 string.
 
+  # "0, 1, 2, 3"
+  Mojo::Collection->new(0, 1, undef, 2, '', 3)->compact->join(', ');
+
 =head2 each
 
   my @elements = $collection->each;
@@ -172,9 +201,10 @@ 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";
+    my ($e, $num) = @_;
+    say "$num: $e";
   });
 
 =head2 first
@@ -188,7 +218,11 @@ 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 contains the word "mojo"
+  my $interesting = $collection->first(qr/mojo/i);
+
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
 
 =head2 flatten
 
@@ -197,6 +231,9 @@ callback and is also available as C<$_>.
 Flatten nested collections/arrays recursively and create a new collection with
 all elements.
 
+  # "1, 2, 3, 4, 5, 6, 7"
+  Mojo::Collection->new(1, [2, [3, 4], 5, [6]], 7)->flatten->join(', ');
+
 =head2 grep
 
   my $new = $collection->grep(qr/foo/);
@@ -207,8 +244,12 @@ 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);
 
+  # Find all values that are greater than 5
+  my $greater = $collection->grep(sub { $_ > 5 });
+
 =head2 join
 
   my $stream = $collection->join;
@@ -216,7 +257,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
 
@@ -227,12 +269,18 @@ Return the last element in collection.
 =head2 map
 
   my $new = $collection->map(sub {...});
+  my $new = $collection->map($method);
+  my $new = $collection->map($method, @args);
 
-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<$_>.
+Evaluate callback for, or call method on, 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 });
+  # Longer version
+  my $new = $collection->map(sub { $_->$method(@args) });
+
+  # Append the word "mojo" to all values
+  my $mojoified = $collection->map(sub { $_ . 'mojo' });
 
 =head2 new
 
@@ -240,16 +288,19 @@ callback and is also available as C<$_>.
 
 Construct a new array-based L<Mojo::Collection> object.
 
-=head2 pluck
+=head2 reduce
 
-  my $new = $collection->pluck($method);
-  my $new = $collection->pluck($method, @args);
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
 
-Call method on each element in collection and create a new collection from the
-results.
+Reduce elements in collection with callback, the first element will be used as
+initial value if none has been provided.
 
-  # Equal to but more convenient than
-  my $new = $collection->map(sub { $_->$method(@args) });
+  # 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
 
@@ -263,6 +314,9 @@ Create a new collection with all elements in reverse order.
 
 Create a new collection with all selected elements.
 
+  # "B C E"
+  Mojo::Collection->new('A', 'B', 'C', 'D', 'E')->slice(1, 2, 4)->join(' ');
+
 =head2 shuffle
 
   my $new = $collection->shuffle;
@@ -283,7 +337,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
 
@@ -291,36 +346,20 @@ from the results.
 
 Alias for L<Mojo::Base/"tap">.
 
-=head2 uniq
-
-  my $new = $collection->uniq;
-
-Create a new collection without duplicate elements.
+=head2 to_array
 
-=head1 AUTOLOAD
+  my $array = $collection->to_array;
 
-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">.
+Turn collection into array reference.
 
-  push @$collection, Mojo::DOM->new("<div><h1>$_</h1></div>") for 1 .. 9;
-  say $collection->find('h1')->type('h2')->prepend_content('Test ')->root;
-
-=head1 OPERATORS
-
-L<Mojo::Collection> overloads the following operators.
-
-=head2 bool
-
-  my $bool = !!$collection;
-
-True or false, depending on if the collection is empty.
+=head2 uniq
 
-=head2 stringify
+  my $new = $collection->uniq;
 
-  my $str = "$collection";
+Create a new collection without duplicate elements.
 
-Stringify elements in collection and L</"join"> them with newlines.
+  # "foo bar baz"
+  Mojo::Collection->new('foo', 'bar', 'bar', 'baz')->uniq->join(' ');
 
 =head1 SEE ALSO
 
@@ -18,12 +18,10 @@ sub body_size {
   my $self = shift;
 
   # Check for existing Content-Lenght header
-  my $content_len = $self->headers->content_length;
-  return $content_len if $content_len;
+  if (my $len = $self->headers->content_length) { return $len }
 
   # Calculate length of whole body
-  my $boundary_len = length($self->build_boundary) + 6;
-  my $len          = $boundary_len - 2;
+  my $len = my $boundary_len = length($self->build_boundary) + 6;
   $len += $_->header_size + $_->body_size + $boundary_len for @{$self->parts};
 
   return $len;
@@ -33,16 +31,15 @@ sub build_boundary {
   my $self = shift;
 
   # Check for existing boundary
-  if (defined(my $boundary = $self->boundary)) { return $boundary }
+  my $boundary;
+  return $boundary if defined($boundary = $self->boundary);
 
   # Generate and check boundary
-  my $boundary;
   my $size = 1;
-  while (1) {
+  do {
     $boundary = b64_encode join('', map chr(rand 256), 1 .. $size++ * 3);
     $boundary =~ s/\W/X/g;
-    last unless $self->body_contains($boundary);
-  }
+  } while $self->body_contains($boundary);
 
   # Add boundary to Content-Type header
   my $headers = $self->headers;
@@ -90,15 +87,12 @@ sub get_body_chunk {
     $len += $content_len;
 
     # Boundary
-    if (($len + $boundary_len) > $offset) {
-
-      # Last boundary
-      return substr "\x0d\x0a--$boundary--", $offset - $len
-        if $#{$parts} == $i;
-
-      # Middle boundary
-      return substr "\x0d\x0a--$boundary\x0d\x0a", $offset - $len;
+    if ($#$parts == $i) {
+      $boundary .= '--';
+      $boundary_len += 2;
     }
+    return substr "\x0d\x0a--$boundary\x0d\x0a", $offset - $len
+      if ($len + $boundary_len) > $offset;
     $len += $boundary_len;
   }
 }
@@ -4,12 +4,16 @@ use Mojo::Base 'Mojo::EventEmitter';
 use Carp 'croak';
 use Compress::Raw::Zlib qw(WANT_GZIP Z_STREAM_END);
 use Mojo::Headers;
+use Scalar::Util 'looks_like_number';
 
-has [qw(auto_relax expect_close relaxed skip_body)];
+has [qw(auto_decompress auto_relax expect_close relaxed skip_body)];
 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 +21,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') }
@@ -88,7 +89,6 @@ sub parse {
   # Headers
   $self->_parse_until_body(@_);
   return $self if $self->{state} eq 'headers';
-  $self->emit('body') unless $self->{body}++;
 
   # Chunked content
   $self->{real_size} //= 0;
@@ -123,22 +123,21 @@ sub parse {
 
   # Chunked or relaxed content
   if ($self->is_chunked || $self->relaxed) {
-    $self->{size} += length($self->{buffer} //= '');
-    $self->_uncompress($self->{buffer});
+    $self->_decompress($self->{buffer} //= '');
+    $self->{size} += length $self->{buffer};
     $self->{buffer} = '';
+    return $self;
   }
 
   # Normal content
-  else {
-    $self->{size} ||= 0;
-    if ((my $need = ($len ||= 0) - $self->{size}) > 0) {
-      my $len = length $self->{buffer};
-      my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
-      $self->_uncompress($chunk);
-      $self->{size} += length $chunk;
-    }
-    $self->{state} = 'finished' if $len <= $self->progress;
+  $len = 0 unless looks_like_number $len;
+  if ((my $need = $len - ($self->{size} ||= 0)) > 0) {
+    my $len = length $self->{buffer};
+    my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
+    $self->_decompress($chunk);
+    $self->{size} += length $chunk;
   }
+  $self->{state} = 'finished' if $len <= $self->progress;
 
   return $self;
 }
@@ -179,8 +178,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
@@ -207,6 +205,29 @@ sub _build_chunk {
   return $crlf . sprintf('%x', length $chunk) . "\x0d\x0a$chunk";
 }
 
+sub _decompress {
+  my ($self, $chunk) = @_;
+
+  # No compression
+  return $self->emit(read => $chunk)
+    unless $self->auto_decompress && $self->is_compressed;
+
+  # Decompress
+  $self->{post_buffer} .= $chunk;
+  my $gz = $self->{gz}
+    //= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
+  my $status = $gz->inflate(\$self->{post_buffer}, my $out);
+  $self->emit(read => $out) if defined $out;
+
+  # Replace Content-Encoding with Content-Length
+  $self->headers->content_length($gz->total_out)->remove('Content-Encoding')
+    if $status == Z_STREAM_END;
+
+  # Check buffer size
+  @$self{qw(state limit)} = ('finished', 1)
+    if length($self->{post_buffer} // '') > $self->max_buffer_size;
+}
+
 sub _parse_chunked {
   my $self = shift;
 
@@ -250,7 +271,8 @@ sub _parse_chunked_trailing_headers {
   return unless $headers->is_finished;
   $self->{chunk_state} = 'finished';
 
-  # Replace Transfer-Encoding with Content-Length
+  # Take care of leftover and replace Transfer-Encoding with Content-Length
+  $self->{buffer} .= $headers->leftovers;
   $headers->remove('Transfer-Encoding');
   $headers->content_length($self->{real_size}) unless $headers->content_length;
 }
@@ -265,7 +287,6 @@ sub _parse_headers {
   # Take care of leftovers
   my $leftovers = $self->{pre_buffer} = $headers->leftovers;
   $self->{header_size} = $self->{raw_size} - length $leftovers;
-  $self->emit('body') unless $self->{body}++;
 }
 
 sub _parse_until_body {
@@ -273,35 +294,8 @@ sub _parse_until_body {
 
   $self->{raw_size} += length($chunk //= '');
   $self->{pre_buffer} .= $chunk;
-
-  unless ($self->{state}) {
-    $self->{header_size} = $self->{raw_size} - length $self->{pre_buffer};
-    $self->{state}       = 'headers';
-  }
-  $self->_parse_headers if ($self->{state} // '') eq 'headers';
-}
-
-sub _uncompress {
-  my ($self, $chunk) = @_;
-
-  # No compression
-  return $self->emit(read => $chunk)
-    unless $self->is_compressed && $self->auto_relax;
-
-  # Uncompress
-  $self->{post_buffer} .= $chunk;
-  my $gz = $self->{gz}
-    //= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
-  my $status = $gz->inflate(\$self->{post_buffer}, my $out);
-  $self->emit(read => $out) if defined $out;
-
-  # Replace Content-Encoding with Content-Length
-  $self->headers->content_length($gz->total_out)->remove('Content-Encoding')
-    if $status == Z_STREAM_END;
-
-  # Check buffer size
-  @$self{qw(state limit)} = ('finished', 1)
-    if length($self->{post_buffer} // '') > $self->max_buffer_size;
+  $self->_parse_headers if ($self->{state} ||= 'headers') eq 'headers';
+  $self->emit('body') if $self->{state} ne 'headers' && !$self->{body}++;
 }
 
 1;
@@ -379,6 +373,13 @@ Emitted when a new chunk of content arrives.
 
 L<Mojo::Content> implements the following attributes.
 
+=head2 auto_decompress
+
+  my $bool = $content->auto_decompress;
+  $content = $content->auto_decompress($bool);
+
+Decompress content automatically if L</"is_compressed"> is true.
+
 =head2 auto_relax
 
   my $bool = $content->auto_relax;
@@ -79,7 +79,7 @@ Always true.
 
   my $str = "$cookie";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -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,14 +28,14 @@ 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) = @_;
+  my ($selectors, $current, $tree, $pos) = @_;
   while ($current = $current->[3]) {
     return undef if $current->[0] eq 'root' || $current eq $tree;
-    return 1 if _combinator($selectors, $current, $tree);
+    return 1 if _combinator($selectors, $current, $tree, $pos);
   }
   return undef;
 }
@@ -62,32 +55,30 @@ sub _attr {
 }
 
 sub _combinator {
-  my ($selectors, $current, $tree) = @_;
+  my ($selectors, $current, $tree, $pos) = @_;
 
   # Selector
-  my @s = @$selectors;
-  return undef unless my $combinator = shift @s;
-  if ($combinator->[0] ne 'combinator') {
-    return undef unless _selector($combinator, $current);
-    return 1 unless $combinator = shift @s;
+  return undef unless my $c = $selectors->[$pos];
+  if (ref $c) {
+    return undef unless _selector($c, $current);
+    return 1 unless $c = $selectors->[++$pos];
   }
 
   # ">" (parent only)
-  my $c = $combinator->[1];
-  return _parent(\@s, $current, $tree) ? 1 : undef if $c eq '>';
+  return _parent($selectors, $current, $tree, ++$pos) if $c eq '>';
 
   # "~" (preceding siblings)
-  return _sibling(\@s, $current, $tree, 0) ? 1 : undef if $c eq '~';
+  return _sibling($selectors, $current, $tree, 0, ++$pos) if $c eq '~';
 
   # "+" (immediately preceding siblings)
-  return _sibling(\@s, $current, $tree, 1) ? 1 : undef if $c eq '+';
+  return _sibling($selectors, $current, $tree, 1, ++$pos) if $c eq '+';
 
   # " " (ancestor)
-  return _ancestor(\@s, $current, $tree) ? 1 : undef;
+  return _ancestor($selectors, $current, $tree, ++$pos);
 }
 
 sub _compile {
-  my $css = shift;
+  my $css = "$_[0]";
 
   my $pattern = [[]];
   while ($css =~ /$TOKEN_RE/go) {
@@ -101,24 +92,21 @@ sub _compile {
     my $part = $pattern->[-1];
 
     # Empty combinator
-    push @$part, [combinator => ' ']
-      if $part->[-1] && $part->[-1][0] ne 'combinator';
+    push @$part, ' ' if $part->[-1] && ref $part->[-1];
 
     # Tag
-    push @$part, ['element'];
-    my $selector = $part->[-1];
-    my $tag      = '*';
-    $element =~ s/^((?:\\\.|\\\#|[^.#])+)// and $tag = _unescape($1);
-    push @$selector, ['tag', $tag];
+    push @$part, my $selector = [];
+    push @$selector, ['tag', qr/(?:^|:)\Q@{[_unescape($1)]}\E$/]
+      if $element =~ s/^((?:\\\.|\\\#|[^.#])+)// && $1 ne '*';
 
     # 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)
-    push @$selector, ['pc', "$1", $1 eq 'not' ? _compile($2) : $2]
+    push @$selector, ['pc', "$1", $1 eq 'not' ? _compile($2) : _equation($2)]
       while $pc =~ /$PSEUDO_CLASS_RE/go;
 
     # Attributes
@@ -126,14 +114,16 @@ sub _compile {
       while $attrs =~ /$ATTR_RE/go;
 
     # Combinator
-    push @$part, [combinator => $combinator] if $combinator;
+    push @$part, $combinator if $combinator;
   }
 
   return $pattern;
 }
 
+sub _empty { $_[0][0] eq 'comment' || $_[0][0] eq 'pi' }
+
 sub _equation {
-  my $equation = shift;
+  return [] unless my $equation = shift;
 
   # "even"
   return [2, 2] if $equation =~ /^even$/i;
@@ -153,22 +143,22 @@ sub _equation {
 
 sub _match {
   my ($pattern, $current, $tree) = @_;
-  _combinator([reverse @$_], $current, $tree) and return 1 for @$pattern;
+  _combinator([reverse @$_], $current, $tree, 0) and return 1 for @$pattern;
   return undef;
 }
 
 sub _parent {
-  my ($selectors, $current, $tree) = @_;
+  my ($selectors, $current, $tree, $pos) = @_;
   return undef unless my $parent = $current->[3];
-  return undef if $parent->[0] eq 'root';
-  return _combinator($selectors, $parent, $tree);
+  return undef if $parent->[0] eq 'root' || $parent eq $tree;
+  return _combinator($selectors, $parent, $tree, $pos);
 }
 
 sub _pc {
   my ($class, $args, $current) = @_;
 
   # ":empty"
-  return !defined $current->[4] if $class eq 'empty';
+  return !grep { !_empty($_) } @$current[4 .. $#$current] if $class eq 'empty';
 
   # ":root"
   return $current->[3] && $current->[3][0] eq 'root' if $class eq 'root';
@@ -181,7 +171,7 @@ sub _pc {
     if $class eq 'checked';
 
   # ":first-*" or ":last-*" (rewrite with equation)
-  ($class, $args) = $1 ? ("nth-$class", 1) : ("nth-last-$class", '-n+1')
+  ($class, $args) = $1 ? ("nth-$class", [0, 1]) : ("nth-last-$class", [-1, 1])
     if $class =~ s/^(?:(first)|last)-//;
 
   # ":nth-*"
@@ -192,7 +182,6 @@ sub _pc {
     # ":nth-last-*"
     @siblings = reverse @siblings if $class =~ /^nth-last/;
 
-    $args = _equation($args) unless ref $args;
     for my $i (0 .. $#siblings) {
       next if (my $result = $args->[0] * $i + $args->[1]) < 1;
       last unless my $sibling = $siblings[$result - 1];
@@ -232,24 +221,16 @@ 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[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree];
   while (my $current = shift @queue) {
-    my $type = $current->[0];
+    next unless $current->[0] eq 'tag';
 
-    # Tag
-    if ($type eq 'tag') {
-      unshift @queue, @$current[4 .. $#$current];
-      next unless _match($pattern, $current, $tree);
-      $one ? return $current : push @results, $current;
-    }
-
-    # Root
-    elsif ($type eq 'root') { unshift @queue, @$current[1 .. $#$current] }
+    unshift @queue, @$current[4 .. $#$current];
+    next unless _match($pattern, $current, $tree);
+    $one ? return $current : push @results, $current;
   }
 
   return $one ? undef : \@results;
@@ -258,14 +239,11 @@ sub _select {
 sub _selector {
   my ($selector, $current) = @_;
 
-  for my $s (@$selector[1 .. $#$selector]) {
+  for my $s (@$selector) {
     my $type = $s->[0];
 
     # Tag (ignore namespace prefix)
-    if ($type eq 'tag') {
-      my $tag = $s->[1];
-      return undef unless $tag eq '*' || $current->[1] =~ /(?:^|:)\Q$tag\E$/;
-    }
+    if ($type eq 'tag') { return undef unless $current->[1] =~ $s->[1] }
 
     # Attribute
     elsif ($type eq 'attr') { return undef unless _attr(@$s[1, 2], $current) }
@@ -280,17 +258,17 @@ sub _selector {
 }
 
 sub _sibling {
-  my ($selectors, $current, $tree, $immediate) = @_;
+  my ($selectors, $current, $tree, $immediate, $pos) = @_;
 
   my $found;
   for my $sibling (@{_siblings($current)}) {
     return $found if $sibling eq $current;
 
     # "+" (immediately preceding sibling)
-    if ($immediate) { $found = _combinator($selectors, $sibling, $tree) }
+    if ($immediate) { $found = _combinator($selectors, $sibling, $tree, $pos) }
 
     # "~" (preceding sibling)
-    else { return 1 if _combinator($selectors, $sibling, $tree) }
+    else { return 1 if _combinator($selectors, $sibling, $tree, $pos) }
   }
 
   return undef;
@@ -39,8 +39,7 @@ my $TOKEN_RE = qr/
     |
       \?(.*?)\?                                       # Processing Instruction
     |
-      (\s*[^<>\s]+                                    # Tag
-      \s*(?:(?:$ATTR_RE){0,32766})*+)                 # Attributes
+      \s*([^<>\s]+\s*(?:(?:$ATTR_RE){0,32766})*+)     # Tag
     )>
   |
     (<)                                               # Runaway "<"
@@ -54,17 +53,10 @@ my %RAW = map { $_ => 1 } qw(script style);
 my %RCDATA = map { $_ => 1 } qw(title textarea);
 
 # HTML elements with optional end tags
-my %END = (
-  body => ['head'],
-  dd   => [qw(dt dd)],
-  dt   => [qw(dt dd)],
-  rp   => [qw(rt rp)],
-  rt   => [qw(rt rp)]
-);
-$END{$_} = [$_] for qw(optgroup option);
+my %END = (body => 'head', optgroup => 'optgroup', option => 'option');
 
 # HTML elements that break paragraphs
-map { $END{$_} = ['p'] } (
+map { $END{$_} = 'p' } (
   qw(address article aside blockquote dir div dl fieldset footer form h1 h2),
   qw(h3 h4 h5 h6 header hr main menu nav ol p pre section table ul)
 );
@@ -72,6 +64,14 @@ map { $END{$_} = ['p'] } (
 # HTML table elements with optional end tags
 my %TABLE = map { $_ => 1 } qw(colgroup tbody td tfoot th thead tr);
 
+# HTML elements with optional end tags and scoping rules
+my %CLOSE
+  = (li => [{li => 1}, {ul => 1, ol => 1}], tr => [{tr => 1}, {table => 1}]);
+$CLOSE{$_} = [\%TABLE, {table => 1}] for qw(colgroup tbody tfoot thead);
+$CLOSE{$_} = [{dd => 1, dt => 1}, {dl    => 1}] for qw(dd dt);
+$CLOSE{$_} = [{rp => 1, rt => 1}, {ruby  => 1}] for qw(rp rt);
+$CLOSE{$_} = [{th => 1, td => 1}, {table => 1}] for qw(td th);
+
 # HTML elements without end tags
 my %EMPTY = map { $_ => 1 } (
   qw(area base br col embed hr img input keygen link menuitem meta param),
@@ -101,7 +101,7 @@ my %BLOCK = map { $_ => 1 } (
 );
 
 sub parse {
-  my ($self, $html) = @_;
+  my ($self, $html) = (shift, "$_[0]");
 
   my $xml = $self->xml;
   my $current = my $tree = ['root'];
@@ -117,10 +117,10 @@ sub parse {
     if (defined $tag) {
 
       # End
-      if ($tag =~ /^\s*\/\s*(.+)/) { _end($xml ? $1 : lc $1, $xml, \$current) }
+      if ($tag =~ /^\/\s*(\S+)/) { _end($xml ? $1 : lc $1, $xml, \$current) }
 
       # Start
-      elsif ($tag =~ m!([^\s/]+)([\s\S]*)!) {
+      elsif ($tag =~ m!^([^\s/]+)([\s\S]*)!) {
         my ($start, $attr) = ($xml ? $1 : lc $1, $2);
 
         # Attributes
@@ -171,17 +171,6 @@ sub parse {
 
 sub render { _render($_[0]->tree, $_[0]->xml) }
 
-sub _close {
-  my ($current, $allowed, $scope) = @_;
-
-  # Close allowed parent elements in scope
-  my $parent = $$current;
-  while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
-    _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
-    $parent = $parent->[3];
-  }
-}
-
 sub _end {
   my ($end, $xml, $current) = @_;
 
@@ -273,22 +262,17 @@ sub _start {
 
   # Autoclose optional HTML elements
   if (!$xml && $$current->[0] ne 'root') {
-    if (my $end = $END{$start}) { _end($_, 0, $current) for @$end }
-
-    # "li"
-    elsif ($start eq 'li') { _close($current, {li => 1}, {ul => 1, ol => 1}) }
+    if (my $end = $END{$start}) { _end($end, 0, $current) }
 
-    # "colgroup", "thead", "tbody" and "tfoot"
-    elsif ($start eq 'colgroup' || $start =~ /^t(?:head|body|foot)$/) {
-      _close($current, \%TABLE, {table => 1});
-    }
+    elsif (my $close = $CLOSE{$start}) {
+      my ($allowed, $scope) = @$close;
 
-    # "tr"
-    elsif ($start eq 'tr') { _close($current, {tr => 1}, {table => 1}) }
-
-    # "th" and "td"
-    elsif ($start eq 'th' || $start eq 'td') {
-      _close($current, {$_ => 1}, {table => 1}) for qw(th td);
+      # Close allowed parent elements in scope
+      my $parent = $$current;
+      while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
+        _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
+        $parent = $parent->[3];
+      }
     }
   }
 
@@ -312,13 +296,13 @@ 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
 
 L<Mojo::DOM::HTML> is the HTML/XML engine used by L<Mojo::DOM> and based on
-the L<HTML Living Standard|http://www.whatwg.org/html> as well as the
+the L<HTML Living Standard|https://html.spec.whatwg.org> as well as the
 L<Extensible Markup Language (XML) 1.0|http://www.w3.org/TR/xml/>.
 
 =head1 ATTRIBUTES
@@ -10,26 +10,28 @@ use overload
 # "Fry: This snow is beautiful. I'm glad global warming never happened.
 #  Leela: Actually, it did. But thank God nuclear winter canceled it out."
 use Carp 'croak';
-use List::Util 'first';
 use Mojo::Collection;
 use Mojo::DOM::CSS;
 use Mojo::DOM::HTML;
-use Mojo::Util 'squish';
+use Mojo::Util qw(deprecated squish);
 use Scalar::Util qw(blessed weaken);
 
+# DEPRECATED in Tiger Face!
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  deprecated "Mojo::DOM::AUTOLOAD ($method) is DEPRECATED"
+    . ' in favor of Mojo::DOM::children';
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
-  # Search children of current element
   my $children = $self->children($method);
   return @$children > 1 ? $children : $children->[0] if @$children;
   croak qq{Can't locate object method "$method" via package "$package"};
 }
 
+# DEPRECATED in Tiger Face!
 sub DESTROY { }
 
 sub all_contents { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
@@ -39,13 +41,12 @@ sub all_text { shift->_all_text(1, @_) }
 sub ancestors { _select($_[0]->_collect($_[0]->_ancestors), $_[1]) }
 
 sub append { shift->_add(1, @_) }
-
 sub append_content { shift->_content(1, 0, @_) }
 
 sub at {
   my $self = shift;
   return undef unless my $result = $self->_css->select_one(@_);
-  return _tag($self, $result, $self->xml);
+  return $self->_build($result, $self->xml);
 }
 
 sub attr {
@@ -60,16 +61,13 @@ 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;
 }
 
-sub children {
-  my $self = shift;
-  return _select(
-    $self->_collect(grep { $_->[0] eq 'tag' } _nodes($self->tree)), @_);
-}
+sub children { _select($_[0]->_collect(_nodes($_[0]->tree, 1)), $_[1]) }
 
 sub content {
   my $self = shift;
@@ -90,6 +88,9 @@ sub contents { $_[0]->_collect(_nodes($_[0]->tree)) }
 
 sub find { $_[0]->_collect(@{$_[0]->_css->select($_[1])}) }
 
+sub following { _select($_[0]->_collect(@{$_[0]->_siblings(1)->[1]}), $_[1]) }
+sub following_siblings { $_[0]->_collect(@{$_[0]->_siblings->[1]}) }
+
 sub match { $_[0]->_css->match($_[1]) ? $_[0] : undef }
 
 sub namespace {
@@ -118,41 +119,48 @@ sub new {
   return @_ ? $self->parse(@_) : $self;
 }
 
-sub next { shift->_siblings->[1][0] }
-sub next_sibling { shift->_siblings(0, 1)->[1][0] }
+sub next         { $_[0]->_maybe($_[0]->_siblings(1, 0)->[1]) }
+sub next_sibling { $_[0]->_maybe($_[0]->_siblings(0, 0)->[1]) }
 
 sub node { shift->tree->[0] }
 
 sub parent {
   my $self = shift;
   return undef if $self->tree->[0] eq 'root';
-  return _tag($self, $self->_parent, $self->xml);
+  return $self->_build($self->_parent, $self->xml);
 }
 
-sub parse { shift->_delegate(parse => shift) }
+sub parse { shift->_delegate(parse => @_) }
 
-sub prepend { shift->_add(0, @_) }
+sub preceding { _select($_[0]->_collect(@{$_[0]->_siblings(1)->[0]}), $_[1]) }
+sub preceding_siblings { $_[0]->_collect(@{$_[0]->_siblings->[0]}) }
 
+sub prepend { shift->_add(0, @_) }
 sub prepend_content { shift->_content(0, 0, @_) }
 
-sub previous { shift->_siblings->[0][-1] }
-sub previous_sibling { shift->_siblings(0, 1)->[0][-1] }
+sub previous         { $_[0]->_maybe($_[0]->_siblings(1, -1)->[0]) }
+sub previous_sibling { $_[0]->_maybe($_[0]->_siblings(0, -1)->[0]) }
 
 sub remove { shift->replace('') }
 
 sub replace {
   my ($self, $new) = @_;
   return $self->parse($new) if (my $tree = $self->tree)->[0] eq 'root';
-  return $self->_replace($self->_parent, $tree, $self->_parse("$new"));
+  return $self->_replace($self->_parent, $tree, $self->_parse($new));
 }
 
 sub root {
   my $self = shift;
   return $self unless my $tree = $self->_ancestors(1);
-  return _tag($self, $tree, $self->xml);
+  return $self->_build($tree, $self->xml);
 }
 
-sub siblings { _select(Mojo::Collection->new(@{_siblings($_[0], 1)}), $_[1]) }
+# DEPRECATED in Tiger Face!
+sub siblings {
+  deprecated 'Mojo::DOM::siblings is DEPRECATED';
+  my $siblings = $_[0]->_siblings(1);
+  return _select($_[0]->_collect(@{$siblings->[0]}, @{$siblings->[1]}), $_[1]);
+}
 
 sub strip {
   my $self = shift;
@@ -176,6 +184,27 @@ sub type {
   return $self;
 }
 
+# DEPRECATED in Tiger Face!
+sub val {
+  deprecated 'Mojo::DOM::val is DEPRECATED';
+  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]')->map('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, @_) }
 
@@ -188,7 +217,7 @@ sub _add {
 
   my $parent = $self->_parent;
   splice @$parent, _offset($parent, $tree) + $offset, 0,
-    _link($self->_parse("$new"), $parent);
+    _link($self->_parse($new), $parent);
 
   return $self;
 }
@@ -222,10 +251,12 @@ sub _ancestors {
   return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1];
 }
 
+sub _build { shift->new->tree(shift)->xml(shift) }
+
 sub _collect {
   my $self = shift;
   my $xml  = $self->xml;
-  return Mojo::Collection->new(map { _tag($self, $_, $xml) } @_);
+  return Mojo::Collection->new(map { $self->_build($_, $xml) } @_);
 }
 
 sub _content {
@@ -239,7 +270,7 @@ sub _content {
 
   $start  = $start  ? ($#$tree + 1) : _start($tree);
   $offset = $offset ? $#$tree       : 0;
-  splice @$tree, $start, $offset, _link($self->_parse("$new"), $tree);
+  splice @$tree, $start, $offset, _link($self->_parse($new), $tree);
 
   return $self;
 }
@@ -268,9 +299,12 @@ sub _link {
   return @new;
 }
 
+sub _maybe { $_[1] ? $_[0]->_build($_[1], $_[0]->xml) : undef }
+
 sub _nodes {
   return unless my $tree = shift;
-  return @$tree[_start($tree) .. $#$tree];
+  my @nodes = @$tree[_start($tree) .. $#$tree];
+  return shift() ? grep { $_->[0] eq 'tag' } @nodes : @nodes;
 }
 
 sub _offset {
@@ -297,25 +331,23 @@ sub _select {
 }
 
 sub _siblings {
-  my ($self, $merge, $all) = @_;
+  my ($self, $tags, $i) = @_;
 
-  return $merge ? [] : [[], []] unless my $parent = $self->parent;
+  return [] unless my $parent = $self->parent;
 
   my $tree = $self->tree;
   my (@before, @after, $match);
-  for my $child ($parent->contents->each) {
-    ++$match and next if $child->tree eq $tree;
-    next unless $all || $child->node eq 'tag';
-    $match ? push @after, $child : push @before, $child;
+  for my $node (_nodes($parent->tree)) {
+    ++$match and next if !$match && $node eq $tree;
+    next if $tags && $node->[0] ne 'tag';
+    $match ? push @after, $node : push @before, $node;
   }
 
-  return $merge ? [@before, @after] : [\@before, \@after];
+  return defined $i ? [$before[$i], $after[$i]] : [\@before, \@after];
 }
 
 sub _start { $_[0][0] eq 'root' ? 1 : 4 }
 
-sub _tag { shift->new->tree(shift)->xml(shift) }
-
 sub _text {
   my ($nodes, $recurse, $trim) = @_;
 
@@ -361,8 +393,8 @@ sub _wrap {
 
   # Find innermost tag
   my $current;
-  my $first = $new = $self->_parse("$new");
-  $current = $first while $first = first { $_->[0] eq 'tag' } _nodes($first);
+  my $first = $new = $self->_parse($new);
+  $current = $first while $first = (_nodes($first, 1))[0];
   return $self unless $current;
 
   # Wrap content
@@ -391,19 +423,15 @@ 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;
-  say $dom->find('p')->text;
-  say $dom->find('[id]')->attr('id');
-
-  # Walk
-  say $dom->div->p->[0]->text;
-  say $dom->div->children('p')->first->{id};
+  say $dom->find('p')->map('text')->join("\n");
+  say $dom->find('[id]')->map(attr => 'id')->join("\n");
 
   # Iterate
-  $dom->find('p[id]')->each(sub { say $_->{id} });
+  $dom->find('p[id]')->reverse->each(sub { say $_->{id} });
 
   # Loop
   for my $e ($dom->find('p[id]')->each) {
@@ -411,8 +439,8 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
   }
 
   # Modify
-  $dom->div->p->last->append('<p id="c">C</p>');
-  $dom->find(':not(p)')->strip;
+  $dom->find('div p')->last->append('<p id="c">456</p>');
+  $dom->find(':not(p)')->map('strip');
 
   # Render
   say "$dom";
@@ -430,14 +458,12 @@ are lowercased and selectors need to be lowercase as well.
 
   my $dom = Mojo::DOM->new('<P ID="greeting">Hi!</P>');
   say $dom->at('p')->text;
-  say $dom->p->{id};
 
 If XML processing instructions are found, the parser will automatically switch
 into XML mode and everything becomes case sensitive.
 
   my $dom = Mojo::DOM->new('<?xml version="1.0"?><P ID="greeting">Hi!</P>');
   say $dom->at('P')->text;
-  say $dom->P->{ID};
 
 XML detection can also be disabled with the L</"xml"> method.
 
@@ -459,8 +485,8 @@ 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>')
-    ->all_contents->grep(sub { $_->node eq 'comment' })->remove->first;
+  $dom->parse('<p><!-- Test --><b>123<!-- 456 --></b></p>')
+    ->all_contents->grep(sub { $_->node eq 'comment' })->map('remove')->first;
 
 =head2 all_text
 
@@ -471,10 +497,10 @@ Extract all text content from DOM structure, smart whitespace trimming is
 enabled by default.
 
   # "foo bar baz"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->all_text;
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->all_text;
 
   # "foo\nbarbaz\n"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->all_text(0);
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->all_text(0);
 
 =head2 ancestors
 
@@ -486,7 +512,7 @@ L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
 All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
   # List types of ancestor elements
-  say $dom->ancestors->type;
+  say $dom->ancestors->map('type')->join("\n");
 
 =head2 append
 
@@ -494,31 +520,34 @@ 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
 
   $dom = $dom->append_content('<p>I ♥ Mojolicious!</p>');
 
-Append HTML/XML fragment or raw content (depending on node type) to this
+Append HTML/XML fragment (for C<root> and C<tag> nodes) or raw content 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
 
-  my $result = $dom->at('html title');
+  my $result = $dom->at('div > p');
 
 Find first element in DOM structure matching the CSS selector and return it as
 a L<Mojo::DOM> object or return C<undef> if none could be found. All selectors
@@ -529,15 +558,15 @@ 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.
 
   # List id attributes
-  say $dom->find('*')->attr('id')->compact;
+  say $dom->find('*')->map(attr => 'id')->compact->join("\n");
 
 =head2 children
 
@@ -556,26 +585,27 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
   my $str = $dom->content;
   $dom    = $dom->content('<p>I ♥ Mojolicious!</p>');
 
-Return this node's content or replace it with HTML/XML fragment or raw content
-(depending on node type).
+Return this node's content or replace it with HTML/XML fragment (for C<root>
+and C<tag> nodes) or raw content.
 
-  # "<b>test</b>"
-  $dom->parse('<div><b>test</b></div>')->div->content;
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->at('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;
+  # "<p><i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->content('<i>123</i>')->root;
 
-  # " A "
-  $dom->parse('<!-- A --><br>')->contents->first->content;
+  # "<div><h1></h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('')->root;
 
-  # "<!-- B --><br>"
-  $dom->parse('<!-- A --><br>')->contents->first->content(' B ')->root;
+  # " Test "
+  $dom->parse('<!-- Test --><br>')->contents->first->content;
 
-  # "<p><i>B</i></p>"
-  $dom->parse('<p>A</p>')->at('p')->content('<i>B</i>')->root;
+  # "<div><!-- 123 -->456</div>"
+  $dom->parse('<div><!-- Test -->456</div>')
+    ->at('div')->contents->first->content(' 123 ')->root;
 
 =head2 contents
 
@@ -585,14 +615,14 @@ 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
 
-  my $collection = $dom->find('html title');
+  my $collection = $dom->find('div > p');
 
 Find all elements in DOM structure matching the CSS selector and return a
 L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
@@ -602,17 +632,53 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
   my $id = $dom->find('div')->[23]{id};
 
   # Extract information from multiple elements
-  my @headers = $dom->find('h1, h2, h3')->text->each;
-  my @links   = $dom->find('a[href]')->attr('href')->each;
+  my @headers = $dom->find('h1, h2, h3')->map('text')->each;
+
+  # Count all the different tags
+  my $hash = $dom->find('*')->reduce(sub { $a->{$b->type}++; $a }, {});
+
+  # Find elements with a class that contains dots
+  my @divs = $dom->find('div.foo\.bar')->each;
+
+=head2 following
+
+  my $collection = $dom->following;
+  my $collection = $dom->following('div > p');
+
+Find all sibling elements after this node matching the CSS selector and return
+a L<Mojo::Collection> object containing these elements as L<Mojo::DOM>
+objects. All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # List types of sibling elements after this node
+  say $dom->following->map('type')->join("\n");
+
+=head2 following_siblings
+
+  my $collection = $dom->following_siblings;
+
+Return a L<Mojo::Collection> object containing the sibling nodes after this
+node as L<Mojo::DOM> objects.
+
+  # "C"
+  $dom->parse('<p>A</p><!-- B -->C')
+    ->at('p')->following_siblings->last->content;
 
 =head2 match
 
-  my $result = $dom->match('html title');
+  my $result = $dom->match('div > p');
 
-Match the CSS selector against this element and return it as a L<Mojo::DOM>
-object or return C<undef> if it didn't match. All selectors from
+Match the CSS selector against this element and return the L<Mojo::DOM> object
+or return C<undef> if it didn't match. All selectors from
 L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
+  # True
+  !!$dom->parse('<p class="a">A</p>')->at('p')->match('.a');
+  !!$dom->parse('<p class="a">A</p>')->at('p')->match('p[class]');
+
+  # False
+  !!$dom->parse('<p class="a">A</p>')->at('p')->match('.b');
+  !!$dom->parse('<p class="a">A</p>')->at('p')->match('p[id]');
+
 =head2 namespace
 
   my $namespace = $dom->namespace;
@@ -640,8 +706,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,8 +717,12 @@ 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')
-    ->next_sibling->next_sibling;
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')
+    ->at('b')->next_sibling->next_sibling;
+
+  # " Test "
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')
+    ->at('b')->next_sibling->content;
 
 =head2 node
 
@@ -677,33 +747,59 @@ Parse HTML/XML fragment with L<Mojo::DOM::HTML>.
   # Parse XML
   my $dom = Mojo::DOM->new->xml(1)->parse($xml);
 
+=head2 preceding
+
+  my $collection = $dom->preceding;
+  my $collection = $dom->preceding('div > p');
+
+Find all sibling elements before this node matching the CSS selector and
+return a L<Mojo::Collection> object containing these elements as L<Mojo::DOM>
+objects. All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # List types of sibling elements before this node
+  say $dom->preceding->map('type')->join("\n");
+
+=head2 preceding_siblings
+
+  my $collection = $dom->preceding_siblings;
+
+Return a L<Mojo::Collection> object containing the sibling nodes before this
+node as L<Mojo::DOM> objects.
+
+  # "A"
+  $dom->parse('A<!-- B --><p>C</p>')
+    ->at('p')->preceding_siblings->first->content;
+
 =head2 prepend
 
   $dom = $dom->prepend('<p>I ♥ Mojolicious!</p>');
 
 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
 
   $dom = $dom->prepend_content('<p>I ♥ Mojolicious!</p>');
 
-Prepend HTML/XML fragment or raw content (depending on node type) to this
-node's content.
+Prepend HTML/XML fragment (for C<root> and C<tag> nodes) or raw content 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 +808,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,8 +819,12 @@ 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')
-    ->previous_sibling->previous_sibling;
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')
+    ->at('b')->previous_sibling->previous_sibling;
+
+  # " Test "
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')
+    ->at('b')->previous_sibling->content;
 
 =head2 remove
 
@@ -733,7 +833,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 +844,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
 
@@ -756,26 +857,14 @@ Replace this node with HTML/XML fragment and return L</"parent">.
 
 Return L<Mojo::DOM> object for root node.
 
-=head2 siblings
-
-  my $collection = $dom->siblings;
-  my $collection = $dom->siblings('div > p');
-
-Find all sibling elements of this node matching the CSS selector and return a
-L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
-All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
-
-  # List types of sibling elements
-  say $dom->siblings->type;
-
 =head2 strip
 
   my $parent = $dom->strip;
 
 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
 
@@ -792,10 +881,10 @@ Extract text content from this element only (not including child elements),
 smart whitespace trimming is enabled by default.
 
   # "foo baz"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->text;
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->text;
 
   # "foo\nbaz\n"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->text(0);
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->text(0);
 
 =head2 to_string
 
@@ -803,8 +892,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>')->at('div b')->to_string;
 
 =head2 tree
 
@@ -822,7 +911,7 @@ carefully since it is very dynamic.
 This element's type.
 
   # List types of child elements
-  say $dom->children->type;
+  say $dom->children->map('type')->join("\n");
 
 =head2 wrap
 
@@ -831,17 +920,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 +939,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
 
@@ -864,17 +953,6 @@ children of the first innermost element.
 Disable HTML semantics in parser and activate case sensitivity, defaults to
 auto detection based on processing instructions.
 
-=head1 AUTOLOAD
-
-In addition to the L</"METHODS"> above, many child elements are also
-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;
-
 =head1 OPERATORS
 
 L<Mojo::DOM> overloads the following operators.
@@ -885,8 +963,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 +979,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
 
@@ -4,7 +4,12 @@ use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
 
 use Time::Local 'timegm';
 
-has 'epoch';
+has epoch => sub {time};
+
+my $RFC3339_RE = qr/
+  ^(\d+)-(\d+)-(\d+)\D+(\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.
@@ -143,7 +184,7 @@ Always true.
 
   my $str = "$date";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -22,26 +22,7 @@ sub emit {
   return $self;
 }
 
-sub emit_safe {
-  my ($self, $name) = (shift, shift);
-
-  if (my $s = $self->{events}{$name}) {
-    warn "-- Emit $name in @{[blessed $self]} safely (@{[scalar @$s]})\n"
-      if DEBUG;
-    for my $cb (@$s) {
-      $self->emit(error => qq{Event "$name" failed: $@})
-        unless eval { $self->$cb(@_); 1 };
-    }
-  }
-  else {
-    warn "-- Emit $name in @{[blessed $self]} safely (0)\n" if DEBUG;
-    die "@{[blessed $self]}: $_[0]" if $name eq 'error';
-  }
-
-  return $self;
-}
-
-sub has_subscribers { !!@{shift->subscribers(shift)} }
+sub has_subscribers { !!@{shift->{events}{shift()} || []} }
 
 sub on {
   my ($self, $name, $cb) = @_;
@@ -125,7 +106,8 @@ L<Mojo::EventEmitter> can emit the following events.
     ...
   });
 
-Emitted for event errors, fatal if unhandled.
+This is a special event for errors, it will not be emitted directly by this
+class but is fatal if unhandled.
 
   $e->on(error => sub {
     my ($e, $err) = @_;
@@ -153,13 +135,6 @@ Subscribe to L</"error"> event.
 
 Emit event.
 
-=head2 emit_safe
-
-  $e = $e->emit_safe('foo');
-  $e = $e->emit_safe('foo', 123);
-
-Emit event safely and emit L</"error"> event on failure.
-
 =head2 has_subscribers
 
   my $bool = $e->has_subscribers('foo');
@@ -213,7 +213,7 @@ Always true.
 
   my $str = "$e";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -8,12 +8,13 @@ has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 };
 # Common headers
 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(Access-Control-Allow-Origin Allow Authorization Cache-Control Connection),
+  qw(Content-Disposition Content-Encoding Content-Language Content-Length),
+  qw(Content-Location Content-Range Content-Type Cookie DNT Date ETag Expect),
+  qw(Expires Host If-Modified-Since If-None-Match Last-Modified Link Location),
+  qw(Origin Proxy-Authenticate Proxy-Authorization Range Sec-WebSocket-Accept),
+  qw(Sec-WebSocket-Extensions Sec-WebSocket-Key Sec-WebSocket-Protocol),
+  qw(Sec-WebSocket-Version Server Set-Cookie Status Strict-Transport-Security),
   qw(TE Trailer Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate)
 );
 for my $header (values %NORMALCASE) {
@@ -89,7 +90,7 @@ sub parse {
     my $line = $1;
 
     # Check line size limit
-    if (length $line > $max) {
+    if ($+[0] > $max) {
       @$self{qw(state limit)} = ('finished', 1);
       return $self;
     }
@@ -223,6 +224,14 @@ Shortcut for the C<Accept-Language> header.
 
 Shortcut for the C<Accept-Ranges> header.
 
+=head2 access_control_allow_origin
+
+  my $origin = $headers->access_control_allow_origin;
+  $headers   = $headers->access_control_allow_origin('*');
+
+Shortcut for the C<Access-Control-Allow-Origin> header from
+L<Cross-Origin Resource Sharing|http://www.w3.org/TR/cors/>.
+
 =head2 add
 
   $headers = $headers->add(Foo => 'one value');
@@ -294,6 +303,13 @@ Shortcut for the C<Content-Disposition> header.
 
 Shortcut for the C<Content-Encoding> header.
 
+=head2 content_language
+
+  my $language = $headers->content_language;
+  $headers     = $headers->content_language('en');
+
+Shortcut for the C<Content-Language> header.
+
 =head2 content_length
 
   my $len  = $headers->content_length;
@@ -301,6 +317,13 @@ Shortcut for the C<Content-Encoding> header.
 
 Shortcut for the C<Content-Length> header.
 
+=head2 content_location
+
+  my $location = $headers->content_location;
+  $headers     = $headers->content_location('http://127.0.0.1/foo');
+
+Shortcut for the C<Content-Location> header.
+
 =head2 content_range
 
   my $range = $headers->content_range;
@@ -341,7 +364,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 +412,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;
@@ -551,6 +581,14 @@ L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
 Shortcut for the C<Status> header from
 L<RFC 3875|http://tools.ietf.org/html/rfc3875>.
 
+=head2 strict_transport_security
+
+  my $policy = $headers->strict_transport_security;
+  $headers   = $headers->strict_transport_security('max-age=31536000');
+
+Shortcut for the C<Strict-Transport-Security> header from
+L<RFC 6797|http://tools.ietf.org/html/rfc6797>.
+
 =head2 te
 
   my $te   = $headers->te;
@@ -141,14 +141,14 @@ Path to C<lib> directory in which L<Mojolicious> is installed.
 =head2 new
 
   my $home = Mojo::Home->new;
-  my $home = Mojo::Home->new('/home/sri/myapp');
+  my $home = Mojo::Home->new('/home/sri/my_app');
 
 Construct a new L<Mojo::Home> object and L</"parse"> home directory if
 necessary.
 
 =head2 parse
 
-  $home = $home->parse('/home/sri/myapp');
+  $home = $home->parse('/home/sri/my_app');
 
 Parse home directory.
 
@@ -185,7 +185,7 @@ Always true.
 
   my $str = "$home";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -2,15 +2,16 @@ package Mojo::IOLoop::Client;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Errno 'EINPROGRESS';
-use IO::Socket::INET;
+use IO::Socket::IP;
 use Mojo::IOLoop;
 use Scalar::Util 'weaken';
-use Socket qw(IPPROTO_TCP SO_ERROR TCP_NODELAY);
+use Socket qw(IPPROTO_TCP SOCK_STREAM TCP_NODELAY);
 
-# IPv6 support requires IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6}
+# Non-blocking name resolution requires Net::DNS::Native
+use constant NDN => $ENV{MOJO_NO_NDN}
   ? 0
-  : eval 'use IO::Socket::IP 0.20 (); 1';
+  : eval 'use Net::DNS::Native 0.14 (); 1';
+my $NDN = NDN ? Net::DNS::Native->new(pool => 5, extra_thread => 1) : undef;
 
 # TLS support requires IO::Socket::SSL
 use constant TLS => $ENV{MOJO_NO_TLS}
@@ -19,6 +20,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 }
@@ -26,14 +34,41 @@ sub DESTROY { shift->_cleanup }
 sub connect {
   my $self = shift;
   my $args = ref $_[0] ? $_[0] : {@_};
+
+  # Timeout
   weaken $self;
-  $self->reactor->next_tick(sub { $self && $self->_connect($args) });
+  my $reactor = $self->reactor;
+  $self->{timer} = $reactor->timer($args->{timeout} || 10,
+    sub { $self->emit(error => 'Connect timeout') });
+
+  # Blocking name resolution
+  $_ && s/[[\]]//g for @$args{qw(address socks_address)};
+  my $address = $args->{socks_address} || ($args->{address} ||= '127.0.0.1');
+  return $reactor->next_tick(sub { $self && $self->_connect($args) })
+    if !NDN || $args->{handle};
+
+  # Non-blocking name resolution
+  my $handle = $self->{dns} = $NDN->getaddrinfo($address, _port($args),
+    {protocol => IPPROTO_TCP, socktype => SOCK_STREAM});
+  $reactor->io(
+    $handle => sub {
+      my $reactor = shift;
+
+      $reactor->remove($self->{dns});
+      my ($err, @res) = $NDN->get_result(delete $self->{dns});
+      return $self->emit(error => "Can't resolve: $err") if $err;
+
+      $args->{addr_info} = \@res;
+      $self->_connect($args);
+    }
+  )->watch($handle, 1, 0);
 }
 
 sub _cleanup {
   my $self = shift;
   return $self unless my $reactor = $self->reactor;
-  $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(timer handle);
+  $NDN->timedout($self->{dns}) if $self->{dns};
+  $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
   return $self;
 }
 
@@ -41,29 +76,52 @@ sub _connect {
   my ($self, $args) = @_;
 
   my $handle;
-  my $reactor = $self->reactor;
-  my $address = $args->{address} ||= 'localhost';
+  my $address = $args->{socks_address} || $args->{address};
   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)
-    );
+    my %options = (PeerAddr => $address, PeerPort => _port($args));
+    %options = (PeerAddrInfo => $args->{addr_info}) if $args->{addr_info};
+    $options{Blocking} = 0;
     $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') });
+    return $self->emit(error => "Can't connect: $@")
+      unless $self->{handle} = $handle = IO::Socket::IP->new(%options);
   }
   $handle->blocking(0);
 
   # Wait for handle to become writable
   weaken $self;
-  $reactor->io($handle => sub { $self->_try($args) })->watch($handle, 0, 1);
+  $self->reactor->io($handle => sub { $self->_ready($args) })
+    ->watch($handle, 0, 1);
+}
+
+sub _port { $_[0]->{socks_port} || $_[0]->{port} || ($_[0]->{tls} ? 443 : 80) }
+
+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 => $! || 'Not connected') 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 {
@@ -71,8 +129,7 @@ sub _tls {
 
   # Connected
   my $handle = $self->{handle};
-  return $self->_cleanup->emit_safe(connect => $handle)
-    if $handle->connect_SSL;
+  return $self->_cleanup->emit(connect => $handle) if $handle->connect_SSL;
 
   # Switch between reading and writing
   my $err = $IO::Socket::SSL::SSL_ERROR;
@@ -80,20 +137,33 @@ 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) = @_;
 
-  return $self->_cleanup->emit_safe(connect => $handle)
+  my $handle = $self->{handle};
+  return $self->_cleanup->emit(connect => $handle)
     if !$args->{tls} || $handle->isa('IO::Socket::SSL');
   return $self->emit(error => 'IO::Socket::SSL 1.84 required for TLS support')
     unless TLS;
@@ -104,7 +174,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 +185,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);
 }
 
@@ -162,7 +232,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely once the connection is established.
+Emitted once the connection is established.
 
 =head2 error
 
@@ -194,8 +264,9 @@ implements the following new ones.
 
   $client->connect(address => '127.0.0.1', port => 3000);
 
-Open a socket connection to a remote host. Note that TLS support depends on
-L<IO::Socket::SSL> (1.84+) and IPv6 support on L<IO::Socket::IP> (0.20+).
+Open a socket connection to a remote host. Note that non-blocking name
+resolution depends on L<Net::DNS::Native> (0.14+), SOCKS5 support on
+L<IO::Socket::Socks> (0.64), and TLS support on L<IO::Socket::SSL> (1.84+).
 
 These options are currently available:
 
@@ -205,7 +276,7 @@ These options are currently available:
 
   address => 'mojolicio.us'
 
-Address or host name of the peer to connect to, defaults to C<localhost>.
+Address or host name of the peer to connect to, defaults to C<127.0.0.1>.
 
 =item handle
 
@@ -225,6 +296,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->(@_) }
 
@@ -145,8 +145,8 @@ emit the following new ones.
     ...
   });
 
-Emitted if an error occurs in one of the steps, breaking the chain, fatal if
-unhandled.
+Emitted if an exception gets thrown in one of the steps, breaking the chain,
+fatal if unhandled.
 
 =head2 finish
 
@@ -250,7 +250,8 @@ Sequentialize multiple events, every time the active event counter reaches
 zero a callback will run, the first one automatically runs during the next
 reactor tick unless it is delayed by incrementing the active event counter.
 This chain will continue until there are no more callbacks, a callback does
-not increment the active event counter or an error occurs in a callback.
+not increment the active event counter or an exception gets thrown in a
+callback.
 
 =head2 wait
 
@@ -4,16 +4,11 @@ use Mojo::Base 'Mojo::EventEmitter';
 use Carp 'croak';
 use File::Basename 'dirname';
 use File::Spec::Functions 'catfile';
-use IO::Socket::INET;
+use IO::Socket::IP;
 use Mojo::IOLoop;
 use Scalar::Util 'weaken';
 use Socket qw(IPPROTO_TCP TCP_NODELAY);
 
-# IPv6 support requires IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6}
-  ? 0
-  : eval 'use IO::Socket::IP 0.20 (); 1';
-
 # TLS support requires IO::Socket::SSL
 use constant TLS => $ENV{MOJO_NO_TLS}
   ? 0
@@ -23,8 +18,8 @@ use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
 
 # To regenerate the certificate run this command (18.04.2012)
 # openssl req -new -x509 -keyout server.key -out server.crt -nodes -days 7300
-my $CERT = catfile dirname(__FILE__), 'server.crt';
-my $KEY  = catfile dirname(__FILE__), 'server.key';
+my $CERT = catfile dirname(__FILE__), 'certs', 'server.crt';
+my $KEY  = catfile dirname(__FILE__), 'certs', 'server.key';
 
 has multi_accept => 50;
 has reactor => sub { Mojo::IOLoop->singleton->reactor };
@@ -38,7 +33,7 @@ sub DESTROY {
 }
 
 sub generate_port {
-  IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
+  IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
 }
 
 sub handle { shift->{handle} }
@@ -59,9 +54,8 @@ sub listen {
 
   # Reuse file descriptor
   my $handle;
-  my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
   if (defined $fd) {
-    $handle = $class->new_from_fd($fd, 'r')
+    $handle = IO::Socket::IP->new_from_fd($fd, 'r')
       or croak "Can't open file descriptor $fd: $!";
   }
 
@@ -76,7 +70,8 @@ sub listen {
     );
     $options{LocalPort} = $port if $port;
     $options{LocalAddr} =~ s/[\[\]]//g;
-    $handle = $class->new(%options) or croak "Can't create listen socket: $@";
+    $handle = IO::Socket::IP->new(%options)
+      or croak "Can't create listen socket: $@";
     $fd = fileno $handle;
     my $reuse = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
     $ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
@@ -105,6 +100,8 @@ sub listen {
   $tls->{SSL_cipher_list} = $args->{tls_ciphers} if $args->{tls_ciphers};
 }
 
+sub port { shift->{handle}->sockport }
+
 sub start {
   my $self = shift;
   weaken $self;
@@ -125,7 +122,7 @@ sub _accept {
     setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
 
     # Start TLS handshake
-    $self->emit_safe(accept => $handle) and next unless my $tls = $self->{tls};
+    $self->emit(accept => $handle) and next unless my $tls = $self->{tls};
     $self->_handshake($self->{handles}{$handle} = $handle)
       if $handle = IO::Socket::SSL->start_SSL($handle, %$tls, SSL_server => 1);
   }
@@ -143,7 +140,7 @@ sub _tls {
   # Accepted
   if ($handle->accept_SSL) {
     $self->reactor->remove($handle);
-    return $self->emit_safe(accept => delete $self->{handles}{$handle});
+    return $self->emit(accept => delete $self->{handles}{$handle});
   }
 
   # Switch between reading and writing
@@ -195,7 +192,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely for each accepted connection.
+Emitted for each accepted connection.
 
 =head1 ATTRIBUTES
 
@@ -238,7 +235,7 @@ Get handle for server.
   $server->listen(port => 3000);
 
 Create a new listen socket. Note that TLS support depends on
-L<IO::Socket::SSL> (1.84+) and IPv6 support on L<IO::Socket::IP> (0.20+).
+L<IO::Socket::SSL> (1.84+).
 
 These options are currently available:
 
@@ -248,7 +245,7 @@ These options are currently available:
 
   address => '127.0.0.1'
 
-Local address to listen on, defaults to all.
+Local address to listen on, defaults to C<0.0.0.0>.
 
 =item backlog
 
@@ -307,6 +304,12 @@ TLS verification mode, defaults to C<0x03>.
 
 =back
 
+=head2 port
+
+  my $port = $server->port;
+
+Get port this server is listening on.
+
 =head2 start
 
   $server->start;
@@ -1,7 +1,7 @@
 package Mojo::IOLoop::Stream;
 use Mojo::Base 'Mojo::EventEmitter';
 
-use Errno qw(EAGAIN ECONNRESET EINTR EPIPE EWOULDBLOCK);
+use Errno qw(EAGAIN ECONNRESET EINTR EWOULDBLOCK);
 use Mojo::IOLoop;
 use Scalar::Util 'weaken';
 
@@ -16,7 +16,7 @@ sub close {
   return unless my $handle  = delete $self->timeout(0)->{handle};
   $reactor->remove($handle);
   close $handle;
-  $self->emit_safe('close');
+  $self->emit('close');
 }
 
 sub close_gracefully {
@@ -76,7 +76,7 @@ sub timeout {
   return $self unless my $timeout = $self->{timeout} = shift;
   weaken $self;
   $self->{timer}
-    = $reactor->timer($timeout => sub { $self->emit_safe('timeout')->close });
+    = $reactor->timer($timeout => sub { $self->emit('timeout')->close });
 
   return $self;
 }
@@ -95,25 +95,18 @@ sub write {
 
 sub _again { $_[0]->reactor->again($_[0]{timer}) if $_[0]{timer} }
 
-sub _error {
+sub _read {
   my $self = shift;
 
+  my $read = $self->{handle}->sysread(my $buffer, 131072, 0);
+  return $read == 0 ? $self->close : $self->emit(read => $buffer)->_again
+    if defined $read;
+
   # Retry
   return if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK;
 
-  # Closed
-  return $self->close if $! == ECONNRESET || $! == EPIPE;
-
-  # Error
-  $self->emit(error => $!)->close;
-}
-
-sub _read {
-  my $self = shift;
-  my $read = $self->{handle}->sysread(my $buffer, 131072, 0);
-  return $self->_error unless defined $read;
-  return $self->close if $read == 0;
-  $self->emit_safe(read => $buffer)->_again;
+  # Closed (maybe real error)
+  $! == ECONNRESET ? $self->close : $self->emit(error => $!)->close;
 }
 
 sub _write {
@@ -121,15 +114,13 @@ sub _write {
 
   my $handle = $self->{handle};
   if (length $self->{buffer}) {
-    my $written = $handle->syswrite($self->{buffer});
-    return $self->_error unless defined $written;
-    $self->emit_safe(write => substr($self->{buffer}, 0, $written, ''));
-    $self->_again;
+    return unless defined(my $written = $handle->syswrite($self->{buffer}));
+    $self->emit(write => substr($self->{buffer}, 0, $written, ''))->_again;
   }
 
-  $self->emit_safe('drain') if !length $self->{buffer};
-  return                    if $self->is_writing;
-  return $self->close       if $self->{graceful};
+  $self->emit('drain') if !length $self->{buffer};
+  return               if $self->is_writing;
+  return $self->close  if $self->{graceful};
   $self->reactor->watch($handle, !$self->{paused}, 0) if $self->{handle};
 }
 
@@ -184,7 +175,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely if the stream gets closed.
+Emitted if the stream gets closed.
 
 =head2 drain
 
@@ -193,7 +184,7 @@ Emitted safely if the stream gets closed.
     ...
   });
 
-Emitted safely once all data has been written.
+Emitted once all data has been written.
 
 =head2 error
 
@@ -211,7 +202,7 @@ Emitted if an error occurs on the stream, fatal if unhandled.
     ...
   });
 
-Emitted safely if new data arrives on the stream.
+Emitted if new data arrives on the stream.
 
 =head2 timeout
 
@@ -220,8 +211,8 @@ Emitted safely if new data arrives on the stream.
     ...
   });
 
-Emitted safely if the stream has been inactive for too long and will get
-closed automatically.
+Emitted if the stream has been inactive for too long and will get closed
+automatically.
 
 =head2 write
 
@@ -230,7 +221,7 @@ closed automatically.
     ...
   });
 
-Emitted safely if new data has been written to the stream.
+Emitted if new data has been written to the stream.
 
 =head1 ATTRIBUTES
 
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaTCCAtKgAwIBAgIJAI+AzotR68CTMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
+VQQGEwJERTEWMBQGA1UECBMNTmllZGVyc2FjaHNlbjESMBAGA1UEBxMJSGFtYmVy
+Z2VuMRQwEgYDVQQKEwtNb2pvbGljaW91czESMBAGA1UEAxMJbG9jYWxob3N0MRsw
+GQYJKoZIhvcNAQkBFgxzcmlAY3Bhbi5vcmcwHhcNMTIwNDE4MTczOTU5WhcNMzIw
+NDEzMTczOTU5WjCBgDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hz
+ZW4xEjAQBgNVBAcTCUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQ
+BgNVBAMTCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG2
+5GSHaRRMyYgd89tEluInMH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz
+51ZIFxq4DtimBftXs9Z9M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiF
+G2ACvVGXSrS16QIDAQABo4HoMIHlMB0GA1UdDgQWBBSrZ+hIlPTgV7xx2O9wzdIO
+/d4osDCBtQYDVR0jBIGtMIGqgBSrZ+hIlPTgV7xx2O9wzdIO/d4osKGBhqSBgzCB
+gDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hzZW4xEjAQBgNVBAcT
+CUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnggkAj4DOi1HrwJMwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAq6MXA7ZeO7B7vAcWxQKeLPKSy
+Jzkb1bC/agaISDbOwuZ1AoQSj6OQHKhNIdY5v/oLQJ0B8wB0dIigqn1WVacDtPgu
+PKSrxpqieDCh2bJ7+dyQIzQHgtZqPHi5k1PyNNXQxC94kPWdFp6PpF0M/y97aCxC
+ZQjKgDfncFWY3FHqUw==
+-----END CERTIFICATE-----
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG25GSHaRRMyYgd89tEluIn
+MH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz51ZIFxq4DtimBftXs9Z9
+M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiFG2ACvVGXSrS16QIDAQAB
+AoGALSdqp6lZ/7nD/c0Uv1CYofySROv3+KFJrl6hadG1/xCP99jVz9pWvMxKBTO/
+2qyrT0ZEitK0nIHLmLOXDVr/rxzbxP/kHmkOLKj45jW31BSap89tUpFjFQXFfjwT
+YnOgOB4+eqQuGwigCqabcQPtFC4fU7Qzk7pdz/kO4FjR0GECQQDdXthCKgS7E5Zy
+qqzjepxYvKgkWPD3G9H6I8LOtiVBdcehflF8Y61OGsEST3pbOhrijhY281VnD1AG
+pNL1rOhDAkEAwuKKTN+2GF3m1mPtGW9jpkP8gU2zcO945U0jxpn2srjQ9oIoB45Y
+gqtE6yybRY4BBd+hMdgeH5dXSwsZW+FMYwJASrFy5LhKylisndoq5cJ8OJDHZyQ/
+ghF4Ax/H3nmlDnZQOpRlqEP1uPHcDXKVxWxQn/rzUe0+9rw681Lv/4ctAwJAfyLO
+2muvHaJUr1QtH0S9m4AKwEfyYiC3m8+BIVTbzagoGki62IMSVtxob4uAGBYVsME9
+JYk5zZ4rgndRKdGGxQJBAIpbdLBKArvnpbYIqNJGG83mUZ/VZaQl0G+S3zGkgre9
+KjIuz10nNMNAKmGRrTbClLtvAQ9MVa3Xjnp+XmxPFho=
+-----END RSA PRIVATE KEY-----
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDaTCCAtKgAwIBAgIJAI+AzotR68CTMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
-VQQGEwJERTEWMBQGA1UECBMNTmllZGVyc2FjaHNlbjESMBAGA1UEBxMJSGFtYmVy
-Z2VuMRQwEgYDVQQKEwtNb2pvbGljaW91czESMBAGA1UEAxMJbG9jYWxob3N0MRsw
-GQYJKoZIhvcNAQkBFgxzcmlAY3Bhbi5vcmcwHhcNMTIwNDE4MTczOTU5WhcNMzIw
-NDEzMTczOTU5WjCBgDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hz
-ZW4xEjAQBgNVBAcTCUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQ
-BgNVBAMTCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnMIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG2
-5GSHaRRMyYgd89tEluInMH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz
-51ZIFxq4DtimBftXs9Z9M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiF
-G2ACvVGXSrS16QIDAQABo4HoMIHlMB0GA1UdDgQWBBSrZ+hIlPTgV7xx2O9wzdIO
-/d4osDCBtQYDVR0jBIGtMIGqgBSrZ+hIlPTgV7xx2O9wzdIO/d4osKGBhqSBgzCB
-gDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hzZW4xEjAQBgNVBAcT
-CUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQBgNVBAMTCWxvY2Fs
-aG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnggkAj4DOi1HrwJMwDAYD
-VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAq6MXA7ZeO7B7vAcWxQKeLPKSy
-Jzkb1bC/agaISDbOwuZ1AoQSj6OQHKhNIdY5v/oLQJ0B8wB0dIigqn1WVacDtPgu
-PKSrxpqieDCh2bJ7+dyQIzQHgtZqPHi5k1PyNNXQxC94kPWdFp6PpF0M/y97aCxC
-ZQjKgDfncFWY3FHqUw==
------END CERTIFICATE-----
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG25GSHaRRMyYgd89tEluIn
-MH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz51ZIFxq4DtimBftXs9Z9
-M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiFG2ACvVGXSrS16QIDAQAB
-AoGALSdqp6lZ/7nD/c0Uv1CYofySROv3+KFJrl6hadG1/xCP99jVz9pWvMxKBTO/
-2qyrT0ZEitK0nIHLmLOXDVr/rxzbxP/kHmkOLKj45jW31BSap89tUpFjFQXFfjwT
-YnOgOB4+eqQuGwigCqabcQPtFC4fU7Qzk7pdz/kO4FjR0GECQQDdXthCKgS7E5Zy
-qqzjepxYvKgkWPD3G9H6I8LOtiVBdcehflF8Y61OGsEST3pbOhrijhY281VnD1AG
-pNL1rOhDAkEAwuKKTN+2GF3m1mPtGW9jpkP8gU2zcO945U0jxpn2srjQ9oIoB45Y
-gqtE6yybRY4BBd+hMdgeH5dXSwsZW+FMYwJASrFy5LhKylisndoq5cJ8OJDHZyQ/
-ghF4Ax/H3nmlDnZQOpRlqEP1uPHcDXKVxWxQn/rzUe0+9rw681Lv/4ctAwJAfyLO
-2muvHaJUr1QtH0S9m4AKwEfyYiC3m8+BIVTbzagoGki62IMSVtxob4uAGBYVsME9
-JYk5zZ4rgndRKdGGxQJBAIpbdLBKArvnpbYIqNJGG83mUZ/VZaQl0G+S3zGkgre9
-KjIuz10nNMNAKmGRrTbClLtvAQ9MVa3Xjnp+XmxPFho=
------END RSA PRIVATE KEY-----
@@ -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
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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> and
-C<MOJO_NO_TLS> environment variables.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
 
@@ -435,7 +436,7 @@ Get L<Mojo::IOLoop::Server> object for id or turn object into an acceptor.
 Open TCP connection with L<Mojo::IOLoop::Client>, takes the same arguments as
 L<Mojo::IOLoop::Client/"connect">.
 
-  # Connect to localhost on port 3000
+  # Connect to 127.0.0.1 on port 3000
   Mojo::IOLoop->client({port => 3000} => sub {
     my ($loop, $err, $stream) = @_;
     ...
@@ -587,7 +588,7 @@ as L<Mojo::IOLoop::Server/"listen">.
     my ($loop, $stream, $id) = @_;
     ...
   });
-  my $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+  my $port = Mojo::IOLoop->acceptor($id)->port;
 
 =head2 singleton
 
@@ -1,7 +1,7 @@
 package Mojo::JSON::Pointer;
 use Mojo::Base -base;
 
-use Scalar::Util 'looks_like_number';
+use Mojo::Util 'deprecated';
 
 has 'data';
 
@@ -11,8 +11,13 @@ sub get      { shift->_pointer(0, @_) }
 sub new { @_ > 1 ? shift->SUPER::new(data => shift) : shift->SUPER::new }
 
 sub _pointer {
-  my ($self, $contains, $data, $pointer) = @_;
-  ($data, $pointer) = ($self->data, $data) unless defined $pointer;
+  my ($self, $contains, $pointer) = @_;
+  my $data = $self->data;
+
+  # DEPRECATED in Tiger Face!
+  deprecated 'Support for data arguments in Mojo::JSON::Pointer is DEPRECATED'
+    and (($pointer, $data) = ($_[3], $pointer))
+    if defined $_[3];
 
   return $data unless $pointer =~ s!^/!!;
   for my $p ($pointer eq '' ? ($pointer) : (split '/', $pointer)) {
@@ -23,7 +28,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];
     }
 
@@ -64,7 +69,7 @@ L<Mojo::JSON::Pointer> implements the following attributes.
   my $data = $pointer->data;
   $pointer = $pointer->data({foo => 'bar'});
 
-Data to be processed.
+Data structure to be processed.
 
 =head1 METHODS
 
@@ -74,40 +79,37 @@ the following new ones.
 =head2 contains
 
   my $bool = $pointer->contains('/foo/1');
-  my $bool = $pointer->contains($data, '/foo/1');
 
-Check if Perl data structure contains a value that can be identified with the
-given JSON Pointer, defaults to using L</data>.
+Check if L</"data"> contains a value that can be identified with the given
+JSON Pointer.
 
   # True
-  $pointer->contains({'♥' => 'mojolicious'}, '/♥');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/foo');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/2');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/♥');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/foo');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/1');
 
   # False
-  $pointer->contains({'♥' => 'mojolicious'}, '/☃');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/bar');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/9');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/☃');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/bar');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/9');
 
 =head2 get
 
   my $value = $pointer->get('/foo/bar');
-  my $value = $pointer->get($data, '/foo/bar');
 
-Extract value identified by the given JSON Pointer, defaults to using
-L</data>.
+Extract value from L</"data"> identified by the given JSON Pointer.
 
   # "mojolicious"
-  $pointer->get({'♥' => 'mojolicious'}, '/♥');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->get('/♥');
 
   # "bar"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/foo');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/foo');
 
   # "4"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/baz/0');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/0');
 
   # "6"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/baz/2');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/2');
 
 =head2 new
 
@@ -4,12 +4,13 @@ use Mojo::Base -base;
 use B;
 use Carp 'croak';
 use Exporter 'import';
-use Mojo::Util;
+use Mojo::Util 'deprecated';
 use Scalar::Util 'blessed';
 
+# DEPRECATED in Tiger Face!
 has 'error';
 
-our @EXPORT_OK = qw(decode_json encode_json j);
+our @EXPORT_OK = qw(decode_json encode_json false from_json j to_json true);
 
 # Literal names
 my $FALSE = bless \(my $false = 0), 'Mojo::JSON::_Bool';
@@ -31,53 +32,64 @@ my %ESCAPE = (
 my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
 for (0x00 .. 0x1f) { $REVERSE{pack 'C', $_} //= sprintf '\u%.4X', $_ }
 
+# DEPRECATED in Tiger Face!
 sub decode {
-  my $self = shift->error(undef);
-  my $value;
-  return $value if eval { $value = _decode(shift); 1 };
-  $self->error(_chomp($@));
-  return undef;
+  shift->error(my $err = _decode(\my $value, pop));
+  return defined $err ? undef : $value;
 }
 
 sub decode_json {
-  my $value;
-  return eval { $value = _decode(shift); 1 } ? $value : croak _chomp($@);
+  my $err = _decode(\my $value, shift);
+  return defined $err ? croak $err : $value;
 }
 
+# DEPRECATED in Tiger Face!
 sub encode { encode_json($_[1]) }
 
 sub encode_json { Mojo::Util::encode 'UTF-8', _encode_value(shift) }
 
-sub false {$FALSE}
+sub false () {$FALSE}
+
+sub from_json {
+  my $err = _decode(\my $value, shift, 1);
+  return defined $err ? croak $err : $value;
+}
 
 sub j {
   return encode_json($_[0]) if ref $_[0] eq 'ARRAY' || ref $_[0] eq 'HASH';
-  return eval { _decode($_[0]) };
+  return eval { decode_json($_[0]) };
+}
+
+# DEPRECATED in Tiger Face!
+sub new {
+  deprecated 'Object-oriented Mojo::JSON API is DEPRECATED';
+  return shift->SUPER::new(@_);
 }
 
-sub true {$TRUE}
+sub to_json { _encode_value(shift) }
 
-sub _chomp { chomp $_[0] ? $_[0] : $_[0] }
+sub true () {$TRUE}
 
 sub _decode {
+  my $valueref = shift;
 
-  # Missing input
-  die "Missing or empty input\n" unless length(my $bytes = shift);
+  eval {
 
-  # Wide characters
-  die "Wide character in input\n" unless utf8::downgrade($bytes, 1);
+    # Missing input
+    die "Missing or empty input\n" unless length(local $_ = shift);
 
-  # UTF-8
-  die "Input is not UTF-8 encoded\n"
-    unless defined(local $_ = Mojo::Util::decode('UTF-8', $bytes));
+    # UTF-8
+    $_ = Mojo::Util::decode 'UTF-8', $_ unless shift;
+    die "Input is not UTF-8 encoded\n" unless defined;
 
-  # Value
-  my $value = _decode_value();
+    # Value
+    $$valueref = _decode_value();
 
-  # Leftover data
-  _exception('Unexpected data') unless m/\G[\x20\x09\x0a\x0d]*\z/gc;
+    # Leftover data
+    m/\G[\x20\x09\x0a\x0d]*\z/gc or _throw('Unexpected data');
+  } ? return undef : chomp $@;
 
-  return $value;
+  return $@;
 }
 
 sub _decode_array {
@@ -94,7 +106,7 @@ sub _decode_array {
     last if m/\G[\x20\x09\x0a\x0d]*\]/gc;
 
     # Invalid character
-    _exception('Expected comma or right square bracket while parsing array');
+    _throw('Expected comma or right square bracket while parsing array');
   }
 
   return \@array;
@@ -106,14 +118,14 @@ sub _decode_object {
 
     # Quote
     m/\G[\x20\x09\x0a\x0d]*"/gc
-      or _exception('Expected string while parsing object');
+      or _throw('Expected string while parsing object');
 
     # Key
     my $key = _decode_string();
 
     # Colon
     m/\G[\x20\x09\x0a\x0d]*:/gc
-      or _exception('Expected colon while parsing object');
+      or _throw('Expected colon while parsing object');
 
     # Value
     $hash{$key} = _decode_value();
@@ -125,7 +137,7 @@ sub _decode_object {
     last if m/\G[\x20\x09\x0a\x0d]*\}/gc;
 
     # Invalid character
-    _exception('Expected comma or right curly bracket while parsing object');
+    _throw('Expected comma or right curly bracket while parsing object');
   }
 
   return \%hash;
@@ -140,9 +152,9 @@ sub _decode_string {
 
   # Invalid character
   unless (m/\G"/gc) {
-    _exception('Unexpected character or invalid escape while parsing string')
+    _throw('Unexpected character or invalid escape while parsing string')
       if m/\G[\x00-\x1f\\]/;
-    _exception('Unterminated string');
+    _throw('Unterminated string');
   }
 
   # Unescape popular characters
@@ -168,11 +180,11 @@ sub _decode_string {
 
         # High surrogate
         ($ord & 0xfc00) == 0xd800
-          or pos($_) = $pos + pos($str), _exception('Missing high-surrogate');
+          or pos($_) = $pos + pos($str), _throw('Missing high-surrogate');
 
         # Low surrogate
         $str =~ m/\G\\u([Dd][C-Fc-f]..)/gc
-          or pos($_) = $pos + pos($str), _exception('Missing low-surrogate');
+          or pos($_) = $pos + pos($str), _throw('Missing low-surrogate');
 
         $ord = 0x10000 + ($ord - 0xd800) * 0x400 + (hex($1) - 0xdc00);
       }
@@ -214,12 +226,11 @@ sub _decode_value {
   return undef if m/\Gnull/gc;
 
   # Invalid character
-  _exception('Expected string, array, object, number, boolean or null');
+  _throw('Expected string, array, object, number, boolean or null');
 }
 
 sub _encode_array {
-  my $array = shift;
-  return '[' . join(',', map { _encode_value($_) } @$array) . ']';
+  '[' . join(',', map { _encode_value($_) } @{$_[0]}) . ']';
 }
 
 sub _encode_object {
@@ -270,7 +281,7 @@ sub _encode_value {
   return _encode_string($value);
 }
 
-sub _exception {
+sub _throw {
 
   # Leading whitespace
   m/\G[\x20\x09\x0a\x0d]*/gc;
@@ -288,7 +299,7 @@ sub _exception {
 
 # Emulate boolean type
 package Mojo::JSON::_Bool;
-use overload '0+' => sub { ${$_[0]} }, '""' => sub { ${$_[0]} }, fallback => 1;
+use overload '""' => sub { ${$_[0]} }, fallback => 1;
 
 1;
 
@@ -302,15 +313,8 @@ Mojo::JSON - Minimalistic JSON
 
   use Mojo::JSON qw(decode_json encode_json);
 
-  # Encode and decode JSON (die on errors)
-  my $bytes = encode_json({foo => [1, 2], bar => 'hello!', baz => \1});
-  my $hash  = decode_json($bytes);
-
-  # Handle errors
-  my $json = Mojo::JSON->new;
-  my $hash = $json->decode($bytes);
-  my $err  = $json->error;
-  say $err ? "Error: $err" : $hash->{message};
+  my $bytes = encode_json {foo => [1, 2], bar => 'hello!', baz => \1};
+  my $hash  = decode_json $bytes;
 
 =head1 DESCRIPTION
 
@@ -321,7 +325,7 @@ It supports normal Perl data types like scalar, array reference, hash
 reference and will try to call the C<TO_JSON> method on blessed references, or
 stringify them if it doesn't exist. Differentiating between strings and
 numbers in Perl is hard, depending on how it has been used, a scalar can be
-both at the same time. The string value gets precedence unless both
+both at the same time. The string value has a higher precedence unless both
 representations are equivalent.
 
   [1, -2, 3]     -> [1, -2, 3]
@@ -341,7 +345,9 @@ their values are true or false.
   \0 -> false
 
 The two Unicode whitespace characters C<u2028> and C<u2029> will always be
-escaped to make JSONP easier.
+escaped to make JSONP easier, and the character C</> to prevent XSS attacks.
+
+  "\x{2028}\x{2029}</script>" -> "\u2028\u2029<\/script>"
 
 =head1 FUNCTIONS
 
@@ -350,65 +356,48 @@ individually.
 
 =head2 decode_json
 
-  my $value = decode_json($bytes);
+  my $value = decode_json $bytes;
 
 Decode JSON to Perl value and die if decoding fails.
 
 =head2 encode_json
 
-  my $bytes = encode_json({foo => 'bar'});
+  my $bytes = encode_json {i => '♥ mojolicious'};
 
 Encode Perl value to JSON.
 
-=head2 j
-
-  my $bytes = j([1, 2, 3]);
-  my $bytes = j({foo => 'bar'});
-  my $value = j($bytes);
-
-Encode Perl data structure (which may only be an array reference or hash
-reference) or decode JSON, an C<undef> return value indicates a bare C<null>
-or that decoding failed.
-
-=head1 ATTRIBUTES
-
-L<Mojo::JSON> implements the following attributes.
-
-=head2 error
-
-  my $err = $json->error;
-  $json   = $json->error('Parser error');
-
-Parser error.
+=head2 false
 
-=head1 METHODS
+  my $false = false;
 
-L<Mojo::JSON> inherits all methods from L<Mojo::Base> and implements the
-following new ones.
+False value, used because Perl has no native equivalent.
 
-=head2 decode
+=head2 from_json
 
-  my $value = $json->decode($bytes);
+  my $value = from_json $chars;
 
-Decode JSON to Perl value and set L</"error"> if decoding failed.
+Decode JSON text that is not C<UTF-8> encoded to Perl value and die if
+decoding fails.
 
-=head2 encode
+=head2 j
 
-  my $bytes = $json->encode({foo => 'bar'});
+  my $bytes = j [1, 2, 3];
+  my $bytes = j {i => '♥ mojolicious'};
+  my $value = j $bytes;
 
-Encode Perl value to JSON.
+Encode Perl data structure (which may only be an array reference or hash
+reference) or decode JSON, an C<undef> return value indicates a bare C<null>
+or that decoding failed.
 
-=head2 false
+=head2 to_json
 
-  my $false = Mojo::JSON->false;
-  my $false = $json->false;
+  my $chars = to_json {i => '♥ mojolicious'};
 
-False value, used because Perl has no native equivalent.
+Encode Perl value to JSON text without C<UTF-8> encoding it.
 
 =head2 true
 
-  my $true = Mojo::JSON->true;
-  my $true = $json->true;
+  my $true = true;
 
 True value, used because Perl has no native equivalent.
 
@@ -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__
@@ -8,15 +8,12 @@ use Mojo::Util 'encode';
 has format => sub { \&_format };
 has handle => sub {
 
-  # File
-  if (my $path = shift->path) {
-    croak qq{Can't open log file "$path": $!}
-      unless open my $file, '>>', $path;
-    return $file;
-  }
-
   # STDERR
-  return \*STDERR;
+  return \*STDERR unless my $path = shift->path;
+
+  # File
+  croak qq{Can't open log file "$path": $!} unless open my $file, '>>', $path;
+  return $file;
 };
 has history => sub { [] };
 has level => 'debug';
@@ -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 {
@@ -53,6 +51,8 @@ sub cookies {
   return $self;
 }
 
+sub every_param { shift->params->every_param(@_) }
+
 sub extract_start_line {
   my ($self, $bufref) = @_;
 
@@ -60,8 +60,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 +106,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 +223,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';
@@ -286,8 +282,8 @@ Mojo::Message::Request - HTTP request
 
 L<Mojo::Message::Request> is a container for HTTP requests based on
 L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
-L<RFC 7231|http://tools.ietf.org/html/rfc7235>,
-L<RFC 7231|http://tools.ietf.org/html/rfc7235> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>,
+L<RFC 7235|http://tools.ietf.org/html/rfc7235> and
 L<RFC 2817|http://tools.ietf.org/html/rfc2817>.
 
 =head1 EVENTS
@@ -357,6 +353,16 @@ Clone request if possible, otherwise return C<undef>.
 
 Access request cookies, usually L<Mojo::Cookie::Request> objects.
 
+=head2 every_param
+
+  my $values = $req->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  say $req->every_param('foo')->[0];
+
 =head2 extract_start_line
 
   my $bool = $req->extract_start_line(\$str);
@@ -396,28 +402,32 @@ Check C<X-Requested-With> header for C<XMLHttpRequest> value.
 =head2 param
 
   my @names       = $req->param;
-  my $foo         = $req->param('foo');
-  my @foo         = $req->param('foo');
+  my $value       = $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.
+If there are multiple values sharing the same name, and you want to access
+more than just the last one, you can use L</"every_param">. 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');
+  # Get parameter names and values
+  my $hash = $req->params->to_hash;
 
 =head2 parse
 
@@ -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,12 +94,12 @@ 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;
   $content->skip_body(1) if $self->code($2)->is_empty;
-  $content->auto_relax(1) unless defined $content->auto_relax;
+  defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
   $content->expect_close(1) if $1 eq '1.0';
   return !!$self->version($1)->message($3);
 }
@@ -190,14 +189,14 @@ implements the following new ones.
   my $code = $res->code;
   $res     = $res->code(200);
 
-HTTP response code.
+HTTP response status code.
 
 =head2 message
 
   my $msg = $res->message;
   $res    = $res->message('OK');
 
-HTTP response message.
+HTTP response status message.
 
 =head1 METHODS
 
@@ -215,8 +214,10 @@ Access response cookies, usually L<Mojo::Cookie::Response> objects.
 =head2 default_message
 
   my $msg = $res->default_message;
+  my $msg = $res->default_message(418);
 
-Generate default response message for code.
+Generate default response message for status code, defaults to using
+L</"code">.
 
 =head2 extract_start_line
 
@@ -61,7 +61,7 @@ sub build_body       { shift->_build('get_body_chunk') }
 sub build_headers    { shift->_build('get_header_chunk') }
 sub build_start_line { shift->_build('get_start_line_chunk') }
 
-sub cookie { shift->_cache(cookie => @_) }
+sub cookie { shift->_cache('cookie', 0, @_) }
 
 sub cookies { croak 'Method "cookies" not implemented by subclass' }
 
@@ -79,6 +79,9 @@ sub error {
   return $self->finish;
 }
 
+sub every_cookie { shift->_cache('cookie', 1, @_) }
+sub every_upload { shift->_cache('upload', 1, @_) }
+
 sub extract_start_line {
   croak 'Method "extract_start_line" not implemented by subclass';
 }
@@ -96,7 +99,8 @@ sub fix_headers {
   # Content-Length or Connection (unless chunked transfer encoding is used)
   my $content = $self->content;
   my $headers = $content->headers;
-  return $self if $content->is_chunked || $headers->content_length;
+  if ($content->is_multipart) { $headers->remove('Content-Length') }
+  elsif ($content->is_chunked || $headers->content_length) { return $self }
   if   ($content->is_dynamic) { $headers->connection('close') }
   else                        { $headers->content_length($self->body_size) }
 
@@ -139,16 +143,11 @@ sub json {
   return $pointer ? Mojo::JSON::Pointer->new($data)->get($pointer) : $data;
 }
 
-sub param { shift->body_params->param(@_) }
-
 sub parse {
-  my ($self, $chunk) = @_;
-
-  # Check message size
-  my $max = $self->max_message_size;
-  return $self->_limit('Maximum message size exceeded', 413)
-    if $max && ($self->{raw_size} += length($chunk //= '')) > $max;
+  my $self = shift;
 
+  return $self if $self->{error};
+  $self->{raw_size} += length(my $chunk = shift // '');
   $self->{buffer} .= $chunk;
 
   # Start line
@@ -168,13 +167,17 @@ sub parse {
   $self->content($self->content->parse(delete $self->{buffer}))
     if $state eq 'content' || $state eq 'finished';
 
+  # Check message size
+  my $max = $self->max_message_size;
+  return $self->_limit('Maximum message size exceeded', 413)
+    if $max && $max < $self->{raw_size};
+
   # Check line size
   return $self->_limit('Maximum line size exceeded', 431)
     if $self->headers->is_limit_exceeded;
 
   # Check buffer size
-  return $self->error(
-    {message => 'Maximum buffer size exceeded', advice => 400})
+  return $self->_limit('Maximum buffer size exceeded', 400)
     if $self->content->is_limit_exceeded;
 
   return $self->emit('progress')->content->is_finished ? $self->finish : $self;
@@ -194,7 +197,7 @@ sub to_string {
   return $self->build_start_line . $self->build_headers . $self->build_body;
 }
 
-sub upload { shift->_cache(upload => @_) }
+sub upload { shift->_cache('upload', 0, @_) }
 
 sub uploads {
   my $self = shift;
@@ -234,10 +237,10 @@ sub _build {
 }
 
 sub _cache {
-  my ($self, $method, $name) = @_;
+  my ($self, $method, $all, $name) = @_;
 
   # Multiple names
-  return map { scalar $self->$method($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->$method($_) } @$name if ref $name eq 'ARRAY';
 
   # Cache objects by name
   $method .= 's';
@@ -246,8 +249,8 @@ sub _cache {
     push @{$self->{$method}{$_->name}}, $_ for @{$self->$method};
   }
 
-  return unless my $objects = $self->{$method}{$name};
-  return wantarray ? @$objects : $objects->[0];
+  my $objects = $self->{$method}{$name} || [];
+  return $all ? $objects : $objects->[-1];
 }
 
 sub _limit {
@@ -427,8 +430,8 @@ 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.
 
-  # Get POST parameter value
-  say $msg->body_params->param('foo');
+  # Get POST parameter names and values
+  my $hash = $msg->body_params->to_hash;
 
 =head2 body_size
 
@@ -456,13 +459,14 @@ Render start line.
 
 =head2 cookie
 
-  my $foo         = $msg->cookie('foo');
-  my @foo         = $msg->cookie('foo');
+  my $cookie      = $msg->cookie('foo');
   my ($foo, $bar) = $msg->cookie(['foo', 'bar']);
 
 Access message cookies, usually L<Mojo::Cookie::Request> or
-L<Mojo::Cookie::Response> objects. Note that this method caches all data, so
-it should not be called before all headers have been received.
+L<Mojo::Cookie::Response> objects. If there are multiple cookies sharing the
+same name, and you want to access more than just the last one, you can use
+L</"every_cookie">. Note that this method caches all data, so it should not be
+called before all headers have been received.
 
   # Get cookie value
   say $msg->cookie('foo')->value;
@@ -486,11 +490,11 @@ whole message body needs to be loaded into memory to parse it, so you have to
 make sure it is not excessively large, there's a 10MB limit by default.
 
   # Perform "find" right away
-  say $msg->dom('h1, h2, h3')->text;
+  say $msg->dom('h1, h2, h3')->map('text')->join("\n");
 
   # Use everything else Mojo::DOM has to offer
   say $msg->dom->at('title')->text;
-  say $msg->dom->html->body->children->type->uniq;
+  say $msg->dom->at('body')->children->map('type')->uniq->join("\n");
 
 =head2 error
 
@@ -500,6 +504,35 @@ make sure it is not excessively large, there's a 10MB limit by default.
 Get or set message error, an C<undef> return value indicates that there is no
 error.
 
+  # Connection error
+  $msg->error({message => 'Connection refused'});
+
+  # Parser error
+  $msg->error({message => 'Maximum message size exceeded', advice => 413});
+
+  # 4xx/5xx response
+  $msg->error({message => 'Internal Server Error', code => 500});
+
+=head2 every_cookie
+
+  my $cookies = $msg->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all message cookies sharing the same name
+as an array reference.
+
+  # Get first cookie value
+  say $msg->every_cookie('foo')->[0]->value;
+
+=head2 every_upload
+
+  my $uploads = $msg->every_upload('foo');
+
+Similar to L</"upload">, but returns all file uploads sharing the same name as
+an array reference.
+
+  # Get content of first uploaded file
+  say $msg->every_upload('foo')->[0]->asset->slurp;
+
 =head2 extract_start_line
 
   my $bool = $msg->extract_start_line(\$str);
@@ -559,7 +592,8 @@ Check if message parser/generator is finished.
 
   my $bool = $msg->is_limit_exceeded;
 
-Check if message has exceeded L</"max_line_size"> or L</"max_message_size">.
+Check if message has exceeded L</"max_line_size">, L</"max_message_size">,
+L<Mojo::Content/"max_buffer_size"> or L<Mojo::Headers/"max_line_size">.
 
 =head2 json
 
@@ -578,19 +612,6 @@ sure it is not excessively large, there's a 10MB limit by default.
   say $msg->json->{foo}{bar}[23];
   say $msg->json('/foo/bar/23');
 
-=head2 param
-
-  my @names       = $msg->param;
-  my $foo         = $msg->param('foo');
-  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.
-
 =head2 parse
 
   $msg = $msg->parse('HTTP/1.1 200 OK...');
@@ -618,13 +639,14 @@ Render whole message.
 
 =head2 upload
 
-  my $foo         = $msg->upload('foo');
-  my @foo         = $msg->upload('foo');
+  my $upload      = $msg->upload('foo');
   my ($foo, $bar) = $msg->upload(['foo', 'bar']);
 
 Access C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects.
-Note that this method caches all data, so it should not be called before the
-entire message body has been received.
+If there are multiple uploads sharing the same name, and you want to access
+more than just the last one, you can use L</"every_upload">. Note that this
+method caches all data, so it should not be called before the entire message
+body has been received.
 
   # Get content of uploaded file
   say $msg->upload('foo')->asset->slurp;
@@ -11,18 +11,16 @@ use Mojo::Util qw(decode encode url_escape url_unescape);
 has charset => 'UTF-8';
 
 sub append {
-  my ($self, @pairs) = @_;
+  my $self = shift;
 
   my $params = $self->params;
-  for (my $i = 0; $i < @pairs; $i += 2) {
-    my $key   = $pairs[$i]     // '';
-    my $value = $pairs[$i + 1] // '';
+  while (my ($name, $value) = splice @_, 0, 2) {
 
     # Single value
-    if (ref $value ne 'ARRAY') { push @$params, $key => $value }
+    if (ref $value ne 'ARRAY') { push @$params, $name => $value }
 
     # Multiple values
-    else { push @$params, $key => (defined $_ ? "$_" : '') for @$value }
+    else { push @$params, $name => (defined $_ ? "$_" : '') for @$value }
   }
 
   return $self;
@@ -31,13 +29,16 @@ 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;
 }
 
+sub every_param { shift->_param(@_) }
+
 sub merge {
   my $self = shift;
   push @{$self->params}, @{$_->params} for @_;
@@ -53,20 +54,14 @@ sub param {
   return sort keys %{$self->to_hash} unless $name;
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
-
-  # Replace values
-  $self->remove($name) if defined $_[0];
-  return $self->append($name => ref $_[0] eq 'ARRAY' ? $_[0] : [@_]) if @_;
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
-  # List values
-  my @values;
-  my $params = $self->params;
-  for (my $i = 0; $i < @$params; $i += 2) {
-    push @values, $params->[$i + 1] if $params->[$i] eq $name;
-  }
+  # Last value
+  return $self->_param($name)->[-1] unless @_;
 
-  return wantarray ? @values : $values[0];
+  # Replace values
+  $self->remove($name);
+  return $self->append($name => ref $_[0] eq 'ARRAY' ? $_[0] : [@_]);
 }
 
 sub params {
@@ -107,38 +102,35 @@ sub parse {
   my $self = shift;
 
   # Pairs
-  if (@_ > 1) { $self->append(@_) }
+  return $self->append(@_) if @_ > 1;
 
   # String
-  else { $self->{string} = shift }
-
+  $self->{string} = shift;
   return $self;
 }
 
 sub remove {
-  my $self = shift;
-  my $name = shift // '';
+  my ($self, $name) = @_;
 
   my $params = $self->params;
-  for (my $i = 0; $i < @$params;) {
-    if ($params->[$i] eq $name) { splice @$params, $i, 2 }
-    else                        { $i += 2 }
-  }
+  my $i      = 0;
+  $params->[$i] eq $name ? splice @$params, $i, 2 : ($i += 2)
+    while $i < @$params;
 
-  return $self->params($params);
+  return $self;
 }
 
 sub to_hash {
   my $self = shift;
 
-  my $params = $self->params;
   my %hash;
+  my $params = $self->params;
   for (my $i = 0; $i < @$params; $i += 2) {
     my ($name, $value) = @{$params}[$i, $i + 1];
 
     # Array
     if (exists $hash{$name}) {
-      $hash{$name} = [$hash{$name}] unless ref $hash{$name} eq 'ARRAY';
+      $hash{$name} = [$hash{$name}] if ref $hash{$name} ne 'ARRAY';
       push @{$hash{$name}}, $value;
     }
 
@@ -159,7 +151,7 @@ sub to_string {
     return url_escape $str, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?';
   }
 
-  # Build pairs;
+  # Build pairs
   my $params = $self->params;
   return '' unless @$params;
   my @pairs;
@@ -179,6 +171,18 @@ sub to_string {
   return join '&', @pairs;
 }
 
+sub _param {
+  my ($self, $name) = @_;
+
+  my @values;
+  my $params = $self->params;
+  for (my $i = 0; $i < @$params; $i += 2) {
+    push @values, $params->[$i + 1] if $params->[$i] eq $name;
+  }
+
+  return \@values;
+}
+
 1;
 
 =encoding utf8
@@ -204,7 +208,7 @@ Mojo::Parameters - Parameters
 
 L<Mojo::Parameters> is a container for form parameters used by L<Mojo::URL>
 and based on L<RFC 3986|http://tools.ietf.org/html/rfc3986> as well as the
-L<HTML Living Standard|http://www.whatwg.org/html>.
+L<HTML Living Standard|https://html.spec.whatwg.org>.
 
 =head1 ATTRIBUTES
 
@@ -248,6 +252,16 @@ Append parameters. Note that this method will normalize the parameters.
 
 Clone parameters.
 
+=head2 every_param
+
+  my $values = $params->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference. Note that this method will normalize the parameters.
+
+  # Get first value
+  say $params->every_param('foo')->[0];
+
 =head2 merge
 
   $params = $params->merge(Mojo::Parameters->new(foo => 'b&ar', baz => 23));
@@ -255,6 +269,9 @@ Clone parameters.
 Merge L<Mojo::Parameters> objects. Note that this method will normalize the
 parameters.
 
+  # "foo=bar&foo=baz"
+  Mojo::Parameters->new('foo=bar')->merge(Mojo::Parameters->new('foo=baz'));
+
 =head2 new
 
   my $params = Mojo::Parameters->new;
@@ -269,18 +286,15 @@ necessary.
 =head2 param
 
   my @names       = $params->param;
-  my $foo         = $params->param('foo');
-  my @foo         = $params->param('foo');
+  my $value       = $params->param('foo');
   my ($foo, $bar) = $params->param(['foo', 'bar']);
   $params         = $params->param(foo => 'ba&r');
   $params         = $params->param(foo => qw(ba&r baz));
   $params         = $params->param(foo => ['ba;r', 'baz']);
 
-Check and replace parameter value. Be aware that if you request a parameter by
-name in scalar context, you will receive only the I<first> value for that
-parameter, if there are multiple values for that name. In list context you
-will receive I<all> of the values for that name. Note that this method will
-normalize the parameters.
+Access parameter values. If there are multiple values sharing the same name,
+and you want to access more than just the last one, you can use
+L</"every_param">. Note that this method will normalize the parameters.
 
 =head2 params
 
@@ -13,26 +13,21 @@ has charset => 'UTF-8';
 sub canonicalize {
   my $self = shift;
 
-  my @parts;
-  for my $part (@{$self->parts}) {
-
-    # ".."
-    if ($part eq '..') {
-      (@parts && $parts[-1] ne '..') ? pop @parts : push @parts, '..';
-    }
-
-    # Something else than "."
-    elsif ($part ne '.' && $part ne '') { push @parts, $part }
+  my $parts = $self->parts;
+  for (my $i = 0; $i <= $#$parts;) {
+    if ($parts->[$i] eq '.' || $parts->[$i] eq '') { splice @$parts, $i, 1 }
+    elsif ($i < 1 || $parts->[$i] ne '..' || $parts->[$i - 1] eq '..') { $i++ }
+    else { splice @$parts, --$i, 2 }
   }
-  $self->trailing_slash(undef) unless @parts;
 
-  return $self->parts(\@parts);
+  return @$parts ? $self : $self->trailing_slash(undef);
 }
 
 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];
@@ -42,10 +37,7 @@ sub clone {
   return $clone;
 }
 
-sub contains {
-  my ($self, $path) = @_;
-  return $path eq '/' || $self->to_route =~ m!^\Q$path\E(?:/|$)!;
-}
+sub contains { $_[1] eq '/' || $_[0]->to_route =~ m!^\Q$_[1]\E(?:/|$)! }
 
 sub leading_slash { shift->_parse(leading_slash => @_) }
 
@@ -86,8 +78,7 @@ sub to_dir {
 
 sub to_route {
   my $clone = shift->clone;
-  my $route = join '/', @{$clone->parts};
-  return "/$route" . ($clone->trailing_slash ? '/' : '');
+  return '/' . join '/', @{$clone->parts}, $clone->trailing_slash ? '' : ();
 }
 
 sub to_string {
@@ -216,6 +207,12 @@ Check if path contains given prefix.
 Path has a leading slash. Note that this method will normalize the path and
 that C<%2F> will be treated as C</> for security reasons.
 
+  # "/foo/bar"
+  Mojo::Path->new('foo/bar')->leading_slash(1);
+
+  # "foo/bar"
+  Mojo::Path->new('/foo/bar')->leading_slash(0);
+
 =head2 merge
 
   $path = $path->merge('/foo/bar');
@@ -310,6 +307,12 @@ Turn path into a string.
 Path has a trailing slash. Note that this method will normalize the path and
 that C<%2F> will be treated as C</> for security reasons.
 
+  # "/foo/bar/"
+  Mojo::Path->new('/foo/bar')->trailing_slash(1);
+
+  # "/foo/bar"
+  Mojo::Path->new('/foo/bar/')->trailing_slash(0);
+
 =head1 OPERATORS
 
 L<Mojo::Path> overloads the following operators.
@@ -30,15 +30,15 @@ sub timer { shift->_timer(0, @_) }
 sub watch {
   my ($self, $handle, $read, $write) = @_;
 
+  my $mode = 0;
+  $mode |= EV::READ  if $read;
+  $mode |= EV::WRITE if $write;
+
   my $fd = fileno $handle;
   my $io = $self->{io}{$fd};
-  my $mode;
-  if ($read && $write) { $mode = EV::READ | EV::WRITE }
-  elsif ($read)  { $mode = EV::READ }
-  elsif ($write) { $mode = EV::WRITE }
-  else           { delete $io->{watcher} }
-  if (my $w = $io->{watcher}) { $w->set($fd, $mode) }
-  elsif ($mode) {
+  if ($mode == 0) { delete $io->{watcher} }
+  elsif (my $w = $io->{watcher}) { $w->set($fd, $mode) }
+  else {
     weaken $self;
     $io->{watcher} = EV::io($fd, $mode, sub { $self->_io($fd, @_) });
   }
@@ -49,9 +49,9 @@ sub watch {
 sub _io {
   my ($self, $fd, $w, $revents) = @_;
   my $io = $self->{io}{$fd};
-  $self->_sandbox('Read', $io->{cb}, 0) if EV::READ &$revents;
+  $self->_sandbox('Read', $io->{cb}, 0) if EV::READ & $revents;
   $self->_sandbox('Write', $io->{cb}, 1)
-    if EV::WRITE &$revents && $self->{io}{$fd};
+    if EV::WRITE & $revents && $self->{io}{$fd};
 }
 
 sub _timer {
@@ -99,11 +99,13 @@ sub timer { shift->_timer(0, @_) }
 sub watch {
   my ($self, $handle, $read, $write) = @_;
 
+  my $mode = 0;
+  $mode |= POLLIN | POLLPRI if $read;
+  $mode |= POLLOUT if $write;
+
   my $poll = $self->_poll;
   $poll->remove($handle);
-  if ($read && $write) { $poll->mask($handle, POLLIN | POLLPRI | POLLOUT) }
-  elsif ($read)  { $poll->mask($handle, POLLIN | POLLPRI) }
-  elsif ($write) { $poll->mask($handle, POLLOUT) }
+  $poll->mask($handle, $mode) if $mode != 0;
 
   return $self;
 }
@@ -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;
@@ -79,7 +78,7 @@ Mojo::Server::CGI - CGI server
   use Mojo::Server::CGI;
 
   my $cgi = Mojo::Server::CGI->new;
-  $cgi->unsubscribe('request')
+  $cgi->unsubscribe('request');
   $cgi->on(request => sub {
     my ($cgi, $tx) = @_;
 
@@ -160,7 +160,7 @@ sub _listen {
       warn "-- Accept (@{[$stream->handle->peerhost]})\n" if DEBUG;
       $stream->timeout($self->inactivity_timeout);
 
-      $stream->on(close => sub { $self->_close($id) });
+      $stream->on(close => sub { $self && $self->_close($id) });
       $stream->on(error =>
           sub { $self && $self->app->log->error(pop) && $self->_close($id) });
       $stream->on(read => sub { $self->_read($id => pop) });
@@ -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.
+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 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 non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -317,10 +317,11 @@ L<Mojo::IOLoop> singleton.
 =head2 listen
 
   my $listen = $daemon->listen;
-  $daemon    = $daemon->listen(['https://localhost:3000']);
+  $daemon    = $daemon->listen(['https://127.0.0.1:8080']);
 
 List of one or more locations to listen on, defaults to the value of the
-C<MOJO_LISTEN> environment variable or C<http://*:3000>.
+C<MOJO_LISTEN> environment variable or C<http://*:3000> (shortcut for
+C<http://0.0.0.0:3000>).
 
   # Listen on all IPv4 interfaces
   $daemon->listen(['http://*:3000']);
@@ -431,7 +432,7 @@ Start accepting connections.
 
   # Listen on random port
   my $id   = $daemon->listen(['http://127.0.0.1'])->start->acceptors->[0];
-  my $port = $daemon->ioloop->acceptor($id)->handle->sockport;
+  my $port = $daemon->ioloop->acceptor($id)->port;
 
 =head2 stop
 
@@ -47,7 +47,8 @@ sub run {
   $ENV{MOJO_MODE} ||= 'production';
 
   # Clean start (to make sure everything works)
-  die "Can't exec: $!" if !$ENV{HYPNOTOAD_REV}++ && !exec $ENV{HYPNOTOAD_EXE};
+  die "Can't exec: $!"
+    if !$ENV{HYPNOTOAD_REV}++ && !exec $^X, $ENV{HYPNOTOAD_EXE};
 
   # Preload application and configure server
   my $prefork = $self->prefork->cleanup(0);
@@ -107,7 +108,7 @@ sub _manage {
     unless ($self->{new}) {
       $log->info('Starting zero downtime software upgrade.');
       die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork);
-      exec($ENV{HYPNOTOAD_EXE}) or die("Can't exec: $!") unless $pid;
+      exec $^X, $ENV{HYPNOTOAD_EXE} or die "Can't exec: $!" unless $pid;
     }
 
     # Timeout
@@ -152,31 +153,31 @@ 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
 C<production> mode.
 
-  $ hypnotoad myapp.pl
+  $ hypnotoad ./myapp.pl
   Server available at http://127.0.0.1:8080.
 
 You can run the same command again for automatic hot deployment.
 
-  $ hypnotoad myapp.pl
+  $ hypnotoad ./myapp.pl
   Starting hot deployment for Hypnotoad server 31841.
 
 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 non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -420,7 +421,7 @@ Configure server from application settings.
 
 =head2 run
 
-  $hypnotoad->run('script/myapp');
+  $hypnotoad->run('script/my_app');
 
 Run server for application.
 
@@ -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
+  $ 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 non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -145,7 +146,7 @@ L<Mojo::Server::Morbo> implements the following attributes.
 =head2 watch
 
   my $watch = $morbo->watch;
-  $morbo    = $morbo->watch(['/home/sri/myapp']);
+  $morbo    = $morbo->watch(['/home/sri/my_app']);
 
 Files and directories to watch for changes, defaults to the application script
 as well as the C<lib> and C<templates> directories in the current working
@@ -165,7 +166,7 @@ its name or C<undef> if there have been no changes.
 
 =head2 run
 
-  $morbo->run('script/myapp');
+  $morbo->run('script/my_app');
 
 Run server for application.
 
@@ -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 non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -2,6 +2,7 @@ package Mojo::Server;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Carp 'croak';
+use Cwd 'abs_path';
 use Mojo::Loader;
 use Mojo::Util 'md5_sum';
 use POSIX;
@@ -15,7 +16,7 @@ sub build_app {
   my ($self, $app) = @_;
   local $ENV{MOJO_EXE};
   return $app->new unless my $e = Mojo::Loader->new->load($app);
-  die ref $e ? $e : qq{Couldn't find application class "$app".\n};
+  die ref $e ? $e : qq{Can't find application class "$app" in \@INC. (@INC)\n};
 }
 
 sub build_tx {
@@ -43,20 +44,16 @@ sub load_app {
 
   # Clean environment (reset FindBin defensively)
   {
-    local $0 = $path;
+    local $0 = $path = abs_path $path;
     require FindBin;
     FindBin->again;
     local $ENV{MOJO_APP_LOADER} = 1;
     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
-    die qq{Couldn't load application from file "$path": $@} if !$app && $@;
+    my $app = eval "package Mojo::Server::Sandbox::@{[md5_sum $path]};"
+      . 'return do($path) || die($@ || $!);';
+    die qq{Can'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');
     $self->app($app);
@@ -77,12 +74,12 @@ sub run { croak 'Method "run" not implemented by subclass' }
 sub setuidgid {
   my $self = shift;
 
-  # Group
+  # Group (make sure secondary groups are reassigned too)
   if (my $group = $self->group) {
     return $self->_log(qq{Group "$group" does not exist.})
       unless defined(my $gid = getgrnam $group);
     return $self->_log(qq{Can't switch to group "$group": $!})
-      unless POSIX::setgid($gid);
+      unless ($( = $) = "$gid $gid") && $) eq "$gid $gid" && $( eq "$gid $gid";
   }
 
   # User
@@ -187,7 +184,7 @@ the following new ones.
 
 =head2 build_app
 
-  my $app = $server->build_app('Mojo::HelloWorld');
+  my $app = $server->build_app('MyApp');
 
 Build application from class.
 
@@ -8,13 +8,13 @@ use Mojo::Util qw(decode encode monkey_patch slurp);
 
 use constant DEBUG => $ENV{MOJO_TEMPLATE_DEBUG} || 0;
 
-has [qw(auto_escape compiled)];
 has [qw(append code prepend template)] => '';
+has [qw(auto_escape compiled)];
 has capture_end   => 'end';
 has capture_start => 'begin';
 has comment_mark  => '#';
 has encoding      => 'UTF-8';
-has escape        => sub { \&Mojo::Util::xml_escape };
+has escape        => sub { \&Mojo::Util::xss_escape };
 has [qw(escape_mark expression_mark trim_mark)] => '=';
 has [qw(line_start replace_mark)] => '%';
 has name      => 'template';
@@ -26,66 +26,61 @@ has tree      => sub { [] };
 sub build {
   my $self = shift;
 
-  my (@lines, $cpst, $multi);
+  my $tree   = $self->tree;
   my $escape = $self->auto_escape;
-  for my $line (@{$self->tree}) {
-    push @lines, '';
-    for (my $j = 0; $j < @{$line}; $j += 2) {
-      my ($op, $value) = @$line[$j, $j + 1];
-      my $newline = chomp($value //= '');
-
-      # Capture end
-      if ($op eq 'cpen') {
-        $lines[-1] .= 'return Mojo::ByteStream->new($_M) }';
 
-        # No following code
-        my $next = $line->[$j + 3];
-        $lines[-1] .= ';' if !defined $next || $next =~ /^\s*$/;
-      }
+  my @blocks = ('');
+  my ($i, $capture, $multi);
+  while (++$i <= @$tree && (my $next = $tree->[$i])) {
+    my ($op, $value) = @{$tree->[$i - 1]};
+    push @blocks, '' and next if $op eq 'line';
+    my $newline = chomp($value //= '');
+
+    # Text (quote and fix line ending)
+    if ($op eq 'text') {
+      $value = join "\n", map { quotemeta $_ } split("\n", $value, -1);
+      $value .= '\n' if $newline;
+      $blocks[-1] .= "\$_M .= \"" . $value . "\";" if length $value;
+    }
 
-      # Text (quote and fix line ending)
-      if ($op eq 'text') {
-        $value = $newline ? quotemeta($value) . '\n' : quotemeta $value;
-        $lines[-1] .= "\$_M .= \"" . $value . "\";" if length $value;
-      }
+    # Code or multiline expression
+    elsif ($op eq 'code' || $multi) { $blocks[-1] .= $value }
 
-      # Code or multiline expression
-      if ($op eq 'code' || $multi) { $lines[-1] .= "$value" }
+    # Capture end
+    elsif ($op eq 'cpen') {
+      $blocks[-1] .= 'return Mojo::ByteStream->new($_M) }';
 
-      # Expression
-      if ($op eq 'expr' || $op eq 'escp') {
+      # No following code
+      $blocks[-1] .= ';' if ($next->[1] // '') =~ /^\s*$/;
+    }
 
-        # Start
-        unless ($multi) {
+    # Expression
+    if ($op eq 'expr' || $op eq 'escp') {
 
-          # Escaped
-          if ($op eq 'escp' && !$escape || $op eq 'expr' && $escape) {
-            $lines[-1] .= "\$_M .= _escape";
-            $lines[-1] .= " scalar $value" if length $value;
-          }
+      # Escaped
+      if (!$multi && ($op eq 'escp' && !$escape || $op eq 'expr' && $escape)) {
+        $blocks[-1] .= "\$_M .= _escape scalar $value";
+      }
 
-          # Raw
-          else { $lines[-1] .= "\$_M .= scalar $value" }
-        }
+      # Raw
+      elsif (!$multi) { $blocks[-1] .= "\$_M .= scalar $value" }
 
-        # Multiline
-        $multi = !(($line->[$j + 2] // '') eq 'text'
-          && ($line->[$j + 3] // '') eq '');
+      # Multiline
+      $multi = !$next || $next->[0] ne 'text';
 
-        # Append semicolon
-        $lines[-1] .= ';' unless $multi || $cpst;
-      }
+      # Append semicolon
+      $blocks[-1] .= ';' unless $multi || $capture;
+    }
 
-      # Capture start
-      if ($cpst) {
-        $lines[-1] .= $cpst;
-        $cpst = undef;
-      }
-      $cpst = " sub { my \$_M = ''; " if $op eq 'cpst';
+    # Capture start
+    if ($op eq 'cpst') { $capture = 1 }
+    elsif ($capture) {
+      $blocks[-1] .= " sub { my \$_M = ''; ";
+      $capture = 0;
     }
   }
 
-  return $self->code(join "\n", @lines)->tree([]);
+  return $self->code(join "\n", @blocks)->tree([]);
 }
 
 sub compile {
@@ -121,7 +116,7 @@ sub parse {
   my ($self, $template) = @_;
 
   # Clean start
-  my $tree = $self->template($template)->tree([])->tree;
+  $self->template($template)->tree(\my @tree);
 
   my $tag     = $self->tag_start;
   my $replace = $self->replace_mark;
@@ -134,6 +129,8 @@ sub parse {
   my $end     = $self->tag_end;
   my $start   = $self->line_start;
 
+  my $line_re
+    = qr/^(\s*)\Q$start\E(?:(\Q$replace\E)|(\Q$cmnt\E)|(\Q$expr\E))?(.*)$/;
   my $token_re = qr/
     (
       \Q$tag\E(?:\Q$replace\E|\Q$cmnt\E)                   # Replace
@@ -145,85 +142,86 @@ sub parse {
       (?:(?<!\w)\Q$cpst\E\s*)?(?:\Q$trim\E)?\Q$end\E       # End
     )
   /x;
-  my $cpen_re = qr/^(\Q$tag\E)(?:\Q$expr\E)?(?:\Q$escp\E)?\s*\Q$cpen\E/;
+  my $cpen_re = qr/^\Q$tag\E(?:\Q$expr\E)?(?:\Q$escp\E)?\s*\Q$cpen\E(.*)$/;
   my $end_re  = qr/^(?:(\Q$cpst\E)\s*)?(\Q$trim\E)?\Q$end\E$/;
 
   # Split lines
-  my $state = 'text';
-  my ($trimming, @capture_token);
+  my $op = 'text';
+  my ($trimming, $capture);
   for my $line (split "\n", $template) {
-    $trimming = 0 if $state eq 'text';
 
     # Turn Perl line into mixed line
-    if ($state eq 'text' && $line !~ s/^(\s*)\Q$start$replace\E/$1$start/) {
-      if ($line =~ s/^(\s*)\Q$start\E(?:(\Q$cmnt\E)|(\Q$expr\E))?//) {
+    if ($op eq 'text' && $line =~ $line_re) {
 
-        # Comment
-        if ($2) { $line = "$tag$2 $trim$end" }
+      # Escaped start
+      if ($2) { $line = "$1$start$5" }
 
-        # Expression or code
-        else { $line = $3 ? "$1$tag$3$line $end" : "$tag$line $trim$end" }
-      }
+      # Comment
+      elsif ($3) { $line = "$tag$3 $trim$end" }
+
+      # Expression or code
+      else { $line = $4 ? "$1$tag$4$5 $end" : "$tag$5 $trim$end" }
     }
 
     # Escaped line ending
-    $line .= "\n" unless $line =~ s/\\\\$/\\\n/ || $line =~ s/\\$//;
+    $line .= "\n" if $line !~ s/\\\\$/\\\n/ && $line !~ s/\\$//;
 
     # Mixed line
-    my @token;
     for my $token (split $token_re, $line) {
 
       # Capture end
-      @capture_token = ('cpen', undef) if $token =~ s/$cpen_re/$1/;
+      ($token, $capture) = ("$tag$1", 1) if $token =~ $cpen_re;
 
       # End
-      if ($state ne 'text' && $token =~ $end_re) {
-        $state = 'text';
+      if ($op ne 'text' && $token =~ $end_re) {
+        $op = 'text';
 
         # Capture start
-        splice @token, -2, 0, 'cpst', undef if $1;
+        splice @tree, -1, 0, ['cpst'] if $1;
 
         # Trim left side
-        if ($2) {
-          $trimming = 1;
-          $self->_trim(\@token);
-        }
+        _trim(\@tree) if ($trimming = $2) && @tree > 1;
 
         # Hint at end
-        push @token, 'text', '';
+        push @tree, ['text', ''];
       }
 
       # Code
-      elsif ($token =~ /^\Q$tag\E$/) { $state = 'code' }
+      elsif ($token eq $tag) { $op = 'code' }
 
       # Expression
-      elsif ($token =~ /^\Q$tag$expr\E$/) { $state = 'expr' }
+      elsif ($token eq "$tag$expr") { $op = 'expr' }
 
       # Expression that needs to be escaped
-      elsif ($token =~ /^\Q$tag$expr$escp\E$/) { $state = 'escp' }
+      elsif ($token eq "$tag$expr$escp") { $op = 'escp' }
 
       # Comment
-      elsif ($token =~ /^\Q$tag$cmnt\E$/) { $state = 'cmnt' }
+      elsif ($token eq "$tag$cmnt") { $op = 'cmnt' }
 
-      # Text
-      else {
+      # Text (comments are just ignored)
+      elsif ($op ne 'cmnt') {
 
         # Replace
         $token = $tag if $token eq "$tag$replace";
 
         # Trim right side (convert whitespace to line noise)
         if ($trimming && $token =~ s/^(\s+)//) {
-          push @token, 'code', $1;
+          push @tree, ['code', $1];
           $trimming = 0;
         }
 
-        # Comments are ignored
-        next if $state eq 'cmnt';
-        push @token, @capture_token, $state, $token;
-        @capture_token = ();
+        # Token (with optional capture end)
+        push @tree, $capture ? ['cpen'] : (), [$op, $token];
+        $capture = 0;
       }
     }
-    push @$tree, \@token;
+
+    # Optimize successive text lines separated by a newline
+    push @tree, ['line'] and next
+      if $tree[-4] && $tree[-4][0] ne 'line'
+      || (!$tree[-3] || $tree[-3][0] ne 'text' || $tree[-3][1] !~ /\n$/)
+      || ($tree[-2][0] ne 'line' || $tree[-1][0] ne 'text');
+    $tree[-3][1] .= pop(@tree)->[1];
   }
 
   return $self;
@@ -253,41 +251,30 @@ sub _line {
 }
 
 sub _trim {
-  my ($self, $line) = @_;
-
-  # Walk line backwards
-  for (my $j = @$line - 4; $j >= 0; $j -= 2) {
-
-    # Skip captures
-    next if $line->[$j] eq 'cpst' || $line->[$j] eq 'cpen';
+  my $tree = shift;
 
-    # Only trim text
-    return unless $line->[$j] eq 'text';
+  # Skip captures
+  my $i = $tree->[-2][0] eq 'cpst' || $tree->[-2][0] eq 'cpen' ? -3 : -2;
 
-    # Convert whitespace text to line noise
-    splice @$line, $j, 0, 'code', $1 if $line->[$j + 1] =~ s/(\s+)$//;
+  # Only trim text
+  return unless $tree->[$i][0] eq 'text';
 
-    # Text left
-    return if length $line->[$j + 1];
-  }
+  # Convert whitespace text to line noise
+  splice @$tree, $i, 0, ['code', $1] if $tree->[$i][1] =~ s/(\s+)$//;
 }
 
 sub _wrap {
   my ($self, $code) = @_;
 
   # Escape function
-  my $escape = $self->escape;
-  monkey_patch $self->namespace, _escape => sub {
-    no warnings 'uninitialized';
-    ref $_[0] eq 'Mojo::ByteStream' ? $_[0] : $escape->("$_[0]");
-  };
+  monkey_patch $self->namespace, '_escape', $self->escape;
 
   # Wrap lines
   my $num = () = $code =~ /\n/g;
   my $head = $self->_line(1);
   $head .= "\npackage @{[$self->namespace]}; use Mojo::Base -strict;";
-  $code = "$head sub { my \$_M = ''; @{[$self->prepend]}; do { $code\n";
-  $code .= $self->_line($num + 1) . "\n@{[$self->append]}; \$_M } };";
+  $code = "$head sub { my \$_M = ''; @{[$self->prepend]}; { $code\n";
+  $code .= $self->_line($num + 1) . "\n@{[$self->append]}; } \$_M };";
 
   warn "-- Code for @{[$self->name]}\n@{[encode 'UTF-8', $code]}\n\n" if DEBUG;
   return $code;
@@ -505,7 +492,7 @@ Encoding used for template files.
   $mt    = $mt->escape(sub {...});
 
 A callback used to escape the results of escaped expressions, defaults to
-L<Mojo::Util/"xml_escape">.
+L<Mojo::Util/"xss_escape">.
 
   $mt->escape(sub {
     my $str = shift;
@@ -603,7 +590,7 @@ Raw unparsed template.
 =head2 tree
 
   my $tree = $mt->tree;
-  $mt      = $mt->tree([['text', 'foo']]);
+  $mt      = $mt->tree([['text', 'foo'], ['line']]);
 
 Template in parsed form. Note that this structure should only be used very
 carefully since it is very dynamic.
@@ -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)
+  # Extended payload (16-bit)
   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;
+    warn "-- Extended 16-bit payload ($len)\n$payload\n" if DEBUG;
+    $frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
   }
 
-  # Extended payload (64bit with 32bit fallback)
+  # Extended payload (64-bit with 32-bit fallback)
   else {
-    warn "-- Extended 64bit payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? (127 | 0b10000000) : 127;
-    $frame .= $prefix;
+    warn "-- Extended 64-bit payload ($len)\n$payload\n" if DEBUG;
+    $frame .= pack 'C', $masked ? (127 | 128) : 127;
     $frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
   }
 
@@ -160,60 +155,54 @@ 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)
+  # Extended payload (16-bit)
   elsif ($len == 126) {
-    return undef unless length $clone > 4;
+    return undef unless length $$buffer > 4;
     $hlen = 4;
-    $len = unpack 'n', substr($clone, 2, 2);
-    warn "-- Extended 16bit payload ($len)\n" if DEBUG;
+    $len = unpack 'n', substr($$buffer, 2, 2);
+    warn "-- Extended 16-bit payload ($len)\n" if DEBUG;
   }
 
-  # Extended payload (64bit with 32bit fallback)
+  # Extended payload (64-bit with 32-bit 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;
+    warn "-- Extended 64-bit payload ($len)\n" if DEBUG;
   }
 
   # Check message size
   $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];
 }
@@ -249,7 +238,7 @@ sub server_close {
 sub server_handshake {
   my $self = shift;
 
-  my $res_headers = $self->res->code(101)->headers;
+  my $res_headers = $self->res->headers;
   $res_headers->upgrade('websocket')->connection('Upgrade');
   my $req_headers = $self->req->headers;
   ($req_headers->sec_websocket_protocol // '') =~ /^\s*([^,]+)/
@@ -365,8 +354,8 @@ Mojo::Transaction::WebSocket - WebSocket transaction
 =head1 DESCRIPTION
 
 L<Mojo::Transaction::WebSocket> is a container for WebSocket transactions
-based on L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64bit
-frames require a Perl with support for quads or they are limited to 32bit.
+based on L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64-bit
+frames require a Perl with support for quads or they are limited to 32-bit.
 
 =head1 EVENTS
 
@@ -501,7 +490,7 @@ object.
   my $bool = $ws->masked;
   $ws      = $ws->masked($bool);
 
-Mask outgoing frames with XOR cipher and a random 32bit key.
+Mask outgoing frames with XOR cipher and a random 32-bit key.
 
 =head2 max_websocket_size
 
@@ -5,7 +5,8 @@ use Carp 'croak';
 use Mojo::Message::Request;
 use Mojo::Message::Response;
 
-has [qw(kept_alive local_address local_port remote_port)];
+has [
+  qw(kept_alive local_address local_port original_remote_address remote_port)];
 has req => sub { Mojo::Message::Request->new };
 has res => sub { Mojo::Message::Response->new };
 
@@ -18,7 +19,7 @@ sub client_close {
     $res->error({message => 'Premature connection close'});
   }
 
-  # 400/500
+  # 4xx/5xx
   elsif ($res->is_status_class(400) || $res->is_status_class(500)) {
     $res->error({message => $res->message, code => $res->code});
   }
@@ -46,20 +47,12 @@ sub is_writing { (shift->{state} // 'write') eq 'write' }
 sub remote_address {
   my $self = shift;
 
-  # New address
-  if (@_) {
-    $self->{remote_address} = shift;
-    return $self;
-  }
+  return $self->original_remote_address(@_) if @_;
+  return $self->original_remote_address unless $self->req->reverse_proxy;
 
   # Reverse proxy
-  if ($self->req->reverse_proxy) {
-    return $self->{forwarded_for} if $self->{forwarded_for};
-    my $forwarded = $self->req->headers->header('X-Forwarded-For') // '';
-    $forwarded =~ /([^,\s]+)$/ and return $self->{forwarded_for} = $1;
-  }
-
-  return $self->{remote_address};
+  return ($self->req->headers->header('X-Forwarded-For') // '')
+    =~ /([^,\s]+)$/ ? $1 : $self->original_remote_address;
 }
 
 sub resume       { shift->_state(qw(write resume)) }
@@ -155,6 +148,13 @@ Local interface address.
 
 Local interface port.
 
+=head2 original_remote_address
+
+  my $address = $tx->original_remote_address;
+  $tx         = $tx->original_remote_address('127.0.0.1');
+
+Remote interface address.
+
 =head2 remote_port
 
   my $port = $tx->remote_port;
@@ -217,6 +217,12 @@ Connection identifier or socket.
 Return transaction error or C<undef> if there is no error, commonly used
 together with L</"success">.
 
+  # Check for different kinds of errors
+  if (my $err = $tx->error) {
+    die "$err->{code} response: $err->{message}" if $err->{code};
+    die "Connection error: $err->{message}";
+  }
+
 =head2 is_finished
 
   my $bool = $tx->is_finished;
@@ -246,7 +252,9 @@ Resume transaction.
   my $address = $tx->remote_address;
   $tx         = $tx->remote_address('127.0.0.1');
 
-Remote interface address.
+Same as L</"original_remote_address"> or the last value of the
+C<X-Forwarded-For> header if L</"req"> has been performed through a reverse
+proxy.
 
 =head2 server_close
 
@@ -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;
 }
 
@@ -296,7 +293,7 @@ Clone this URL.
 
 Normalized version of L</"host"> and L</"port">.
 
-  # "xn--da5b0n.net:8080"
+  # "xn--n3h.net:8080"
   Mojo::URL->new('http://☃.net:8080/test')->host_port;
 
 =head2 ihost
@@ -306,7 +303,7 @@ Normalized version of L</"host"> and L</"port">.
 
 Host part of this URL in punycode format.
 
-  # "xn--da5b0n.net"
+  # "xn--n3h.net"
   Mojo::URL->new('http://☃.net')->ihost;
 
 =head2 is_abs
@@ -315,6 +312,15 @@ Host part of this URL in punycode format.
 
 Check if URL is absolute.
 
+  # True
+  Mojo::URL->new('http://example.com')->is_abs;
+  Mojo::URL->new('http://example.com/test/index.html')->is_abs;
+
+  # False
+  Mojo::URL->new('test/index.html')->is_abs;
+  Mojo::URL->new('/test/index.html')->is_abs;
+  Mojo::URL->new('//example.com/test/index.html')->is_abs;
+
 =head2 new
 
   my $url = Mojo::URL->new;
@@ -347,6 +353,9 @@ Parse relative or absolute URL.
 Path part of this URL, relative paths will be merged with the existing path,
 defaults to a L<Mojo::Path> object.
 
+  # "perldoc"
+  Mojo::URL->new('http://example.com/perldoc/Mojo')->path->parts->[0];
+
   # "http://example.com/DOM/HTML"
   Mojo::URL->new('http://example.com/perldoc/Mojo')->path('/DOM/HTML');
 
@@ -362,6 +371,9 @@ defaults to a L<Mojo::Path> object.
 
 Normalized version of L</"path"> and L</"query">.
 
+  # "/test?a=1&b=2"
+  Mojo::URL->new('http://example.com/test?a=1&b=2')->path_query;
+
 =head2 protocol
 
   my $proto = $url->protocol;
@@ -440,7 +452,7 @@ Always true.
 
   my $str = "$url";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -4,6 +4,7 @@ use Mojo::Base -base;
 use Mojo::Cookie::Request;
 use Mojo::Path;
 
+has extracting      => 1;
 has max_cookie_size => 4096;
 
 sub add {
@@ -40,6 +41,9 @@ sub empty { delete shift->{jar} }
 
 sub extract {
   my ($self, $tx) = @_;
+
+  return unless $self->extracting;
+
   my $url = $tx->req->url;
   for my $cookie (@{$tx->res->cookies}) {
 
@@ -146,6 +150,14 @@ L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
 
 L<Mojo::UserAgent::CookieJar> implements the following attributes.
 
+=head2 extracting
+
+  my $bool = $jar->extracting;
+  $jar     = $jar->extracting($bool);
+
+Allow L</"extract"> to L</"add"> new cookies to the jar, defaults to a true
+value.
+
 =head2 max_cookie_size
 
   my $size = $jar->max_cookie_size;
@@ -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.
 
@@ -38,21 +38,21 @@ sub _restart {
   weaken $server->app($self->app)->{app};
   my $port = $self->{port} ? ":$self->{port}" : '';
   $self->{port} = $server->listen(["$proto://127.0.0.1$port"])
-    ->start->ioloop->acceptor($server->acceptors->[0])->handle->sockport;
+    ->start->ioloop->acceptor($server->acceptors->[0])->port;
 
   # Non-blocking
   $server = $self->{nb_server} = Mojo::Server::Daemon->new(silent => 1);
   weaken $server->app($self->app)->{app};
   $port = $self->{nb_port} ? ":$self->{nb_port}" : '';
   $self->{nb_port} = $server->listen(["$proto://127.0.0.1$port"])
-    ->start->ioloop->acceptor($server->acceptors->[0])->handle->sockport;
+    ->start->ioloop->acceptor($server->acceptors->[0])->port;
 }
 
 sub _url {
   my ($self, $nb) = (shift, shift);
   $self->_restart(0, @_) if !$self->{server} || @_;
   my $port = $nb ? $self->{nb_port} : $self->{port};
-  return Mojo::URL->new("$self->{proto}://localhost:$port/");
+  return Mojo::URL->new("$self->{proto}://127.0.0.1:$port/");
 }
 
 1;
@@ -95,9 +95,9 @@ implements the following new ones.
 =head2 app
 
   my $app = Mojo::UserAgent::Server->app;
-            Mojo::UserAgent::Server->app(MyApp->new);
+            Mojo::UserAgent::Server->app(Mojolicious->new);
   my $app = $server->app;
-  $server = $server->app(MyApp->new);
+  $server = $server->app(Mojolicious->new);
 
 Application this server handles, instance specific applications override the
 global default.
@@ -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;
@@ -75,9 +75,11 @@ sub redirect {
   return undef unless grep { $_ == $code } 301, 302, 303, 307, 308;
 
   # Fix location without authority and/or scheme
-  return unless my $location = $res->headers->location;
+  return undef unless my $location = $res->headers->location;
   $location = Mojo::URL->new($location);
   $location = $location->base($old->req->url)->to_abs unless $location->is_abs;
+  my $proto = $location->protocol;
+  return undef unless $proto eq 'http' || $proto eq 'https';
 
   # Clone request if necessary
   my $new = Mojo::Transaction::HTTP->new;
@@ -155,19 +157,19 @@ sub _form {
   my ($self, $tx, $form, %options) = @_;
 
   # Check for uploads and force multipart if necessary
-  my $multipart;
+  my $req       = $tx->req;
+  my $headers   = $req->headers;
+  my $multipart = ($headers->content_type // '') =~ m!multipart/form-data!i;
   for my $value (map { ref $_ eq 'ARRAY' ? @$_ : $_ } values %$form) {
     ++$multipart and last if ref $value eq 'HASH';
   }
-  my $req     = $tx->req;
-  my $headers = $req->headers;
-  $headers->content_type('multipart/form-data') if $multipart;
 
   # Multipart
-  if (($headers->content_type // '') eq 'multipart/form-data') {
+  if ($multipart) {
     my $parts = $self->_multipart($options{charset}, $form);
     $req->content(
       Mojo::Content::MultiPart->new(headers => $headers, parts => $parts));
+    _type($headers, 'multipart/form-data');
     return $tx;
   }
 
@@ -175,18 +177,17 @@ sub _form {
   my $p = Mojo::Parameters->new(map { $_ => $form->{$_} } sort keys %$form);
   $p->charset($options{charset}) if defined $options{charset};
   my $method = uc $req->method;
-  if ($method eq 'GET' || $method eq 'HEAD') { $req->url->query->merge($p) }
+  if ($method eq 'GET' || $method eq 'HEAD') { $req->url->query($p) }
   else {
     $req->body($p->to_string);
-    $headers->content_type('application/x-www-form-urlencoded');
+    _type($headers, 'application/x-www-form-urlencoded');
   }
   return $tx;
 }
 
 sub _json {
   my ($self, $tx, $data) = @_;
-  my $headers = $tx->req->body(encode_json($data))->headers;
-  $headers->content_type('application/json') unless $headers->content_type;
+  _type($tx->req->body(encode_json $data)->headers, 'application/json');
   return $tx;
 }
 
@@ -254,6 +255,8 @@ sub _proxy {
   return $proto, $host, $port;
 }
 
+sub _type { $_[0]->content_type($_[1]) unless $_[0]->content_type }
+
 1;
 
 =encoding utf8
@@ -284,6 +287,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 +365,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;
@@ -373,28 +393,51 @@ requests, with support for content generators.
   my $tx = $t->tx(PUT => 'http://example.com');
   $tx->req->content->asset(Mojo::Asset::File->new(path => '/foo.txt'));
 
-  # GET request with query parameters
-  my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'});
+The C<json> content generator uses L<Mojo::JSON> for encoding and sets the
+content type to C<application/json>.
 
   # POST request with "application/json" content
   my $tx = $t->tx(
     POST => 'http://example.com' => json => {a => 'b', c => [1, 2, 3]});
 
+The C<form> content generator will automatically use query parameters for
+C<GET> and C<HEAD> requests.
+
+  # GET request with query parameters
+  my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'});
+
+For all other request methods the C<application/x-www-form-urlencoded> content
+type is used.
+
   # POST request with "application/x-www-form-urlencoded" content
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {a => 'b', c => 'd'});
 
+Parameters may be encoded with the C<charset> option.
+
   # PUT request with Shift_JIS encoded form values
   my $tx = $t->tx(
     PUT => 'example.com' => form => {a => 'b'} => charset => 'Shift_JIS');
 
+An array reference can be used for multiple form values sharing the same name.
+
   # POST request with form values sharing the same name
   my $tx = $t->tx(POST => 'http://example.com' => form => {a => [qw(b c d)]});
 
+A hash reference with a C<content> or C<file> value can be used to switch to
+the C<multipart/form-data> content type for file uploads.
+
   # POST request with "multipart/form-data" content
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {mytext => {content => 'lala'}});
 
+  # POST request with multiple files sharing the same name
+  my $tx = $t->tx(POST => 'http://example.com' =>
+    form => {mytext => [{content => 'first'}, {content => 'second'}]});
+
+The C<file> value should contain the path to the file you want to upload or an
+asset object, like L<Mojo::Asset::File> or L<Mojo::Asset::Memory>.
+
   # POST request with upload streamed from file
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {mytext => {file => '/foo.txt'}});
@@ -404,9 +447,9 @@ requests, with support for content generators.
   my $tx    = $t->tx(
     POST => 'http://example.com' => form => {mytext => {file => $asset}});
 
-  # POST request with multiple files sharing the same name
-  my $tx = $t->tx(POST => 'http://example.com' =>
-    form => {mytext => [{content => 'first'}, {content => 'second'}]});
+A C<filename> value will be generated automatically, but can also be set
+manually if necessary. All remainging values in the hash reference get merged
+into the C<multipart/form-data> content as headers.
 
   # POST request with form values and customized upload (filename and header)
   my $tx = $t->tx(POST => 'http://example.com' => form => {
@@ -419,11 +462,8 @@ requests, with support for content generators.
     }
   });
 
-The C<form> content generator will automatically use query parameters for
-C<GET>/C<HEAD> requests and the C<application/x-www-form-urlencoded> content
-type for everything else. Both get upgraded automatically to using the
-C<multipart/form-data> content type when necessary or when the header has been
-set manually.
+The C<multipart/form-data> content type can also be enforced by setting the
+C<Content-Type> header manually.
 
   # Force "multipart/form-data"
   my $headers = {'Content-Type' => 'multipart/form-data'};
@@ -70,7 +70,7 @@ sub websocket {
 
 sub _cleanup {
   my $self = shift;
-  return unless my $loop = $self->_loop(0);
+  return $self unless my $loop = $self->_loop(0);
 
   # Clean up active connections (by closing them)
   delete $self->{pid};
@@ -78,28 +78,40 @@ sub _cleanup {
 
   # Clean up keep-alive connections
   $loop->remove($_->[1]) for @{delete $self->{queue} || []};
-  $loop = $self->_loop(1);
+  return $self unless $loop = $self->_loop(1);
   $loop->remove($_->[1]) for @{delete $self->{nb_queue} || []};
 
   return $self;
 }
 
 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
@@ -107,8 +119,7 @@ sub _connect {
       return $self->_error($id, $err) if $err;
 
       # Connection established
-      $stream->on(
-        timeout => sub { $self->_error($id, 'Inactivity timeout', 1) });
+      $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
       $stream->on(close => sub { $self && $self->_finish($id, 1) });
       $stream->on(error => sub { $self && $self->_error($id, pop) });
       $stream->on(read => sub { $self->_read($id, pop) });
@@ -127,12 +138,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 +151,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 +199,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;
@@ -226,12 +234,9 @@ sub _enqueue {
 }
 
 sub _error {
-  my ($self, $id, $err, $timeout) = @_;
-
-  if (my $tx = $self->{connections}{$id}{tx}) {
-    $tx->res->error({message => $err});
-  }
-  elsif (!$timeout) { return $self->emit(error => $err) }
+  my ($self, $id, $err) = @_;
+  my $tx = $self->{connections}{$id}{tx};
+  $tx->res->error({message => $err}) if $tx;
   $self->_finish($id, 1);
 }
 
@@ -239,7 +244,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 +298,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)
@@ -314,8 +316,7 @@ sub _start {
     $url->scheme($base->scheme)->authority($base->authority);
   }
 
-  $self->proxy->inject($tx);
-  if (my $jar = $self->cookie_jar) { $jar->inject($tx) }
+  $_ && $_->inject($tx) for $self->proxy, $self->cookie_jar;
 
   # Connect and add request timeout if necessary
   my $id = $self->emit(start => $tx)->_connection($nb, $tx, $cb);
@@ -377,17 +378,24 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
     ->res->json('/results/0/title');
 
   # Extract data from HTML and XML resources
-  say $ua->get('www.perl.org')->res->dom->html->head->title->text;
+  say $ua->get('www.perl.org')->res->dom->at('title')->text;
 
   # Scrape the latest headlines from a news site with CSS selectors
-  say $ua->get('blogs.perl.org')->res->dom('h2 > a')->text->shuffle;
+  say $ua->get('blogs.perl.org')
+    ->res->dom->find('h2 > a')->map('text')->join("\n");
+
+  # Search DuckDuckGo anonymously through Tor
+  $ua->proxy->http('socks://127.0.0.1:9050');
+  say $ua->get('api.3g2upl4pq6kufc4m.onion/?q=mojolicious&format=json')
+    ->res->json('/Abstract');
 
   # IPv6 PUT request with content
   my $tx
     = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hello!');
 
   # Follow redirects to grab the latest Mojolicious release :)
-  $ua->max_redirects(5)->get('latest.mojolicio.us')
+  $ua->max_redirects(5)
+    ->get('https://www.github.com/kraih/mojo/tarball/master')
     ->res->content->asset->move_to('/Users/sri/mojo.tar.gz');
 
   # TLS certificate authentication and JSON POST
@@ -395,18 +403,18 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
     ->post('https://example.com' => json => {top => 'secret'});
 
   # Non-blocking concurrent requests
-  my $delay = Mojo::IOLoop->delay(sub {
-    my ($delay, @titles) = @_;
-    say for @titles;
-  });
-  for my $url ('mojolicio.us', 'cpan.org') {
-    my $end = $delay->begin(0);
-    $ua->get($url => sub {
-      my ($ua, $tx) = @_;
-      $end->($tx->res->dom->at('title')->text);
-    });
-  }
-  $delay->wait;
+  Mojo::IOLoop->delay(
+    sub {
+      my $delay = shift;
+      $ua->get('mojolicio.us' => $delay->begin);
+      $ua->get('cpan.org'     => $delay->begin);
+    },
+    sub {
+      my ($delay, $mojo, $cpan) = @_;
+      say $mojo->res->dom->at('title')->text;
+      say $cpan->res->dom->at('title')->text;
+    }
+  )->wait;
 
   # Non-blocking WebSocket connection sending and receiving JSON messages
   $ua->websocket('ws://example.com/echo.json' => sub {
@@ -424,19 +432,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 non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.14+), 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_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
 
@@ -445,21 +454,6 @@ See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
 L<Mojo::UserAgent> inherits all events from L<Mojo::EventEmitter> and can emit
 the following new ones.
 
-=head2 error
-
-  $ua->on(error => sub {
-    my ($ua, $err) = @_;
-    ...
-  });
-
-Emitted if an error occurs that can't be associated with a transaction, fatal
-if unhandled.
-
-  $ua->on(error => sub {
-    my ($ua, $err) = @_;
-    say "This looks bad: $err";
-  });
-
 =head2 start
 
   $ua->on(start => sub {
@@ -513,11 +507,11 @@ environment variable or C<10>.
   my $cookie_jar = $ua->cookie_jar;
   $ua            = $ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
 
-Cookie jar to use for this user agents requests, defaults to a
+Cookie jar to use for requests performed by this user agent, defaults to a
 L<Mojo::UserAgent::CookieJar> object.
 
-  # Disable cookie jar
-  $ua->cookie_jar(0);
+  # Disable extraction of cookies from responses
+  $ua->cookie_jar->extracting(0);
 
 =head2 inactivity_timeout
 
@@ -580,6 +574,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 +632,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 +673,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 +693,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 +713,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 +733,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 +753,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 +773,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 +793,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) = @_;
@@ -809,8 +814,9 @@ append a callback to perform requests non-blocking.
 
   my $tx = $ua->start(Mojo::Transaction::HTTP->new);
 
-Perform blocking request. You can also append a callback to perform requests
-non-blocking.
+Perform blocking request for a custom L<Mojo::Transaction::HTTP> object, which
+can be prepared manually or with L</"build_tx">. You can also append a
+callback to perform requests non-blocking.
 
   my $tx = $ua->build_tx(GET => 'http://example.com');
   $ua->start($tx => sub {
@@ -828,7 +834,8 @@ non-blocking.
 Open a non-blocking WebSocket connection with transparent handshake, takes the
 same arguments as L<Mojo::UserAgent::Transactor/"websocket">. The callback
 will receive either a L<Mojo::Transaction::WebSocket> or
-L<Mojo::Transaction::HTTP> object.
+L<Mojo::Transaction::HTTP> object, depending on if the handshake was
+successful.
 
   $ua->websocket('ws://example.com/echo' => sub {
     my ($ua, $tx) = @_;
@@ -7,15 +7,14 @@ use Digest::MD5 qw(md5 md5_hex);
 use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
 use Encode 'find_encoding';
 use Exporter 'import';
-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
-use constant MONOTONIC => eval
-  '!!Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC())';
+use constant MONOTONIC =>
+  eval { !!Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) };
 
 # Punycode bootstring parameters
 use constant {
@@ -28,10 +27,14 @@ use constant {
   PC_INITIAL_N    => 128
 };
 
-# To update HTML entities run this command
-# perl examples/entities.pl > lib/Mojo/entities.txt
+# Will be shipping with Perl 5.22
+my $NAME
+  = eval { require Sub::Util; Sub::Util->can('set_subname') } || sub { $_[1] };
+
+# To generate a new HTML entity table run this command
+# perl examples/entities.pl
 my %ENTITIES;
-for my $line (split "\x0a", slurp(catfile dirname(__FILE__), 'entities.txt')) {
+while (my $line = <DATA>) {
   next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/;
   $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2);
 }
@@ -54,7 +57,7 @@ our @EXPORT_OK = (
   qw(md5_sum monkey_patch punycode_decode punycode_encode quote),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
   qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode)
+  qw(xml_escape xor_encode xss_escape)
 );
 
 sub b64_decode { decode_base64($_[0]) }
@@ -66,7 +69,7 @@ sub camelize {
 
   # CamelCase words
   return join '::', map {
-    join '', map { ucfirst lc } split '_', $_
+    join('', map { ucfirst lc } split '_')
   } split '-', $str;
 }
 
@@ -83,17 +86,10 @@ sub decamelize {
   my $str = shift;
   return $str if $str !~ /^[A-Z]/;
 
-  # Module parts
-  my @parts;
-  for my $part (split '::', $str) {
-
-    # snake_case words
-    my @words;
-    push @words, lc $1 while $part =~ s/([A-Z]{1}[^A-Z]*)//;
-    push @parts, join '_', @words;
-  }
-
-  return join '-', @parts;
+  # snake_case words
+  return join '-', map {
+    join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
+  } split '::', $str;
 }
 
 sub decode {
@@ -129,7 +125,7 @@ sub monkey_patch {
   my ($class, %patch) = @_;
   no strict 'refs';
   no warnings 'redefine';
-  *{"${class}::$_"} = $patch{$_} for keys %patch;
+  *{"${class}::$_"} = $NAME->("${class}::$_", $patch{$_}) for keys %patch;
 }
 
 # Direct translation of RFC 3492
@@ -295,7 +291,7 @@ sub tablify {
     for my $i (0 .. $#$row) {
       $row->[$i] =~ s/[\r\n]//g;
       my $len = length $row->[$i];
-      $spec[$i] = $len if $len > ($spec[$i] // 0);
+      $spec[$i] = $len if $len >= ($spec[$i] // 0);
     }
   }
 
@@ -327,8 +323,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;
 }
 
@@ -355,6 +351,11 @@ sub xor_encode {
   return $output .= $buffer ^ substr($key, 0, length $buffer, '');
 }
 
+sub xss_escape {
+  no warnings 'uninitialized';
+  ref $_[0] eq 'Mojo::ByteStream' ? $_[0] : xml_escape("$_[0]");
+}
+
 sub _adapt {
   my ($delta, $numpoints, $firsttime) = @_;
   use integer;
@@ -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
@@ -625,8 +664,9 @@ consecutive groups of whitespace into one space each.
 
   my $time = steady_time;
 
-High resolution time, resilient to time jumps if a monotonic clock is
-available through L<Time::HiRes>.
+High resolution time elapsed from an arbitrary fixed point in the past,
+resilient to time jumps if a monotonic clock is available through
+L<Time::HiRes>.
 
 =head2 tablify
 
@@ -683,8 +723,2247 @@ Escape unsafe characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in string.
 
 XOR encode string with variable length key.
 
+=head2 xss_escape
+
+  my $escaped = xss_escape $str;
+
+Same as L</"xml_escape">, but does not escape L<Mojo::ByteStream> objects.
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+Aacute; U+000C1
+Aacute U+000C1
+aacute; U+000E1
+aacute U+000E1
+Abreve; U+00102
+abreve; U+00103
+ac; U+0223E
+acd; U+0223F
+acE; U+0223E U+00333
+Acirc; U+000C2
+Acirc U+000C2
+acirc; U+000E2
+acirc U+000E2
+acute; U+000B4
+acute U+000B4
+Acy; U+00410
+acy; U+00430
+AElig; U+000C6
+AElig U+000C6
+aelig; U+000E6
+aelig U+000E6
+af; U+02061
+Afr; U+1D504
+afr; U+1D51E
+Agrave; U+000C0
+Agrave U+000C0
+agrave; U+000E0
+agrave U+000E0
+alefsym; U+02135
+aleph; U+02135
+Alpha; U+00391
+alpha; U+003B1
+Amacr; U+00100
+amacr; U+00101
+amalg; U+02A3F
+AMP; U+00026
+AMP U+00026
+amp; U+00026
+amp U+00026
+And; U+02A53
+and; U+02227
+andand; U+02A55
+andd; U+02A5C
+andslope; U+02A58
+andv; U+02A5A
+ang; U+02220
+ange; U+029A4
+angle; U+02220
+angmsd; U+02221
+angmsdaa; U+029A8
+angmsdab; U+029A9
+angmsdac; U+029AA
+angmsdad; U+029AB
+angmsdae; U+029AC
+angmsdaf; U+029AD
+angmsdag; U+029AE
+angmsdah; U+029AF
+angrt; U+0221F
+angrtvb; U+022BE
+angrtvbd; U+0299D
+angsph; U+02222
+angst; U+000C5
+angzarr; U+0237C
+Aogon; U+00104
+aogon; U+00105
+Aopf; U+1D538
+aopf; U+1D552
+ap; U+02248
+apacir; U+02A6F
+apE; U+02A70
+ape; U+0224A
+apid; U+0224B
+apos; U+00027
+ApplyFunction; U+02061
+approx; U+02248
+approxeq; U+0224A
+Aring; U+000C5
+Aring U+000C5
+aring; U+000E5
+aring U+000E5
+Ascr; U+1D49C
+ascr; U+1D4B6
+Assign; U+02254
+ast; U+0002A
+asymp; U+02248
+asympeq; U+0224D
+Atilde; U+000C3
+Atilde U+000C3
+atilde; U+000E3
+atilde U+000E3
+Auml; U+000C4
+Auml U+000C4
+auml; U+000E4
+auml U+000E4
+awconint; U+02233
+awint; U+02A11
+backcong; U+0224C
+backepsilon; U+003F6
+backprime; U+02035
+backsim; U+0223D
+backsimeq; U+022CD
+Backslash; U+02216
+Barv; U+02AE7
+barvee; U+022BD
+Barwed; U+02306
+barwed; U+02305
+barwedge; U+02305
+bbrk; U+023B5
+bbrktbrk; U+023B6
+bcong; U+0224C
+Bcy; U+00411
+bcy; U+00431
+bdquo; U+0201E
+becaus; U+02235
+Because; U+02235
+because; U+02235
+bemptyv; U+029B0
+bepsi; U+003F6
+bernou; U+0212C
+Bernoullis; U+0212C
+Beta; U+00392
+beta; U+003B2
+beth; U+02136
+between; U+0226C
+Bfr; U+1D505
+bfr; U+1D51F
+bigcap; U+022C2
+bigcirc; U+025EF
+bigcup; U+022C3
+bigodot; U+02A00
+bigoplus; U+02A01
+bigotimes; U+02A02
+bigsqcup; U+02A06
+bigstar; U+02605
+bigtriangledown; U+025BD
+bigtriangleup; U+025B3
+biguplus; U+02A04
+bigvee; U+022C1
+bigwedge; U+022C0
+bkarow; U+0290D
+blacklozenge; U+029EB
+blacksquare; U+025AA
+blacktriangle; U+025B4
+blacktriangledown; U+025BE
+blacktriangleleft; U+025C2
+blacktriangleright; U+025B8
+blank; U+02423
+blk12; U+02592
+blk14; U+02591
+blk34; U+02593
+block; U+02588
+bne; U+0003D U+020E5
+bnequiv; U+02261 U+020E5
+bNot; U+02AED
+bnot; U+02310
+Bopf; U+1D539
+bopf; U+1D553
+bot; U+022A5
+bottom; U+022A5
+bowtie; U+022C8
+boxbox; U+029C9
+boxDL; U+02557
+boxDl; U+02556
+boxdL; U+02555
+boxdl; U+02510
+boxDR; U+02554
+boxDr; U+02553
+boxdR; U+02552
+boxdr; U+0250C
+boxH; U+02550
+boxh; U+02500
+boxHD; U+02566
+boxHd; U+02564
+boxhD; U+02565
+boxhd; U+0252C
+boxHU; U+02569
+boxHu; U+02567
+boxhU; U+02568
+boxhu; U+02534
+boxminus; U+0229F
+boxplus; U+0229E
+boxtimes; U+022A0
+boxUL; U+0255D
+boxUl; U+0255C
+boxuL; U+0255B
+boxul; U+02518
+boxUR; U+0255A
+boxUr; U+02559
+boxuR; U+02558
+boxur; U+02514
+boxV; U+02551
+boxv; U+02502
+boxVH; U+0256C
+boxVh; U+0256B
+boxvH; U+0256A
+boxvh; U+0253C
+boxVL; U+02563
+boxVl; U+02562
+boxvL; U+02561
+boxvl; U+02524
+boxVR; U+02560
+boxVr; U+0255F
+boxvR; U+0255E
+boxvr; U+0251C
+bprime; U+02035
+Breve; U+002D8
+breve; U+002D8
+brvbar; U+000A6
+brvbar U+000A6
+Bscr; U+0212C
+bscr; U+1D4B7
+bsemi; U+0204F
+bsim; U+0223D
+bsime; U+022CD
+bsol; U+0005C
+bsolb; U+029C5
+bsolhsub; U+027C8
+bull; U+02022
+bullet; U+02022
+bump; U+0224E
+bumpE; U+02AAE
+bumpe; U+0224F
+Bumpeq; U+0224E
+bumpeq; U+0224F
+Cacute; U+00106
+cacute; U+00107
+Cap; U+022D2
+cap; U+02229
+capand; U+02A44
+capbrcup; U+02A49
+capcap; U+02A4B
+capcup; U+02A47
+capdot; U+02A40
+CapitalDifferentialD; U+02145
+caps; U+02229 U+0FE00
+caret; U+02041
+caron; U+002C7
+Cayleys; U+0212D
+ccaps; U+02A4D
+Ccaron; U+0010C
+ccaron; U+0010D
+Ccedil; U+000C7
+Ccedil U+000C7
+ccedil; U+000E7
+ccedil U+000E7
+Ccirc; U+00108
+ccirc; U+00109
+Cconint; U+02230
+ccups; U+02A4C
+ccupssm; U+02A50
+Cdot; U+0010A
+cdot; U+0010B
+cedil; U+000B8
+cedil U+000B8
+Cedilla; U+000B8
+cemptyv; U+029B2
+cent; U+000A2
+cent U+000A2
+CenterDot; U+000B7
+centerdot; U+000B7
+Cfr; U+0212D
+cfr; U+1D520
+CHcy; U+00427
+chcy; U+00447
+check; U+02713
+checkmark; U+02713
+Chi; U+003A7
+chi; U+003C7
+cir; U+025CB
+circ; U+002C6
+circeq; U+02257
+circlearrowleft; U+021BA
+circlearrowright; U+021BB
+circledast; U+0229B
+circledcirc; U+0229A
+circleddash; U+0229D
+CircleDot; U+02299
+circledR; U+000AE
+circledS; U+024C8
+CircleMinus; U+02296
+CirclePlus; U+02295
+CircleTimes; U+02297
+cirE; U+029C3
+cire; U+02257
+cirfnint; U+02A10
+cirmid; U+02AEF
+cirscir; U+029C2
+ClockwiseContourIntegral; U+02232
+CloseCurlyDoubleQuote; U+0201D
+CloseCurlyQuote; U+02019
+clubs; U+02663
+clubsuit; U+02663
+Colon; U+02237
+colon; U+0003A
+Colone; U+02A74
+colone; U+02254
+coloneq; U+02254
+comma; U+0002C
+commat; U+00040
+comp; U+02201
+compfn; U+02218
+complement; U+02201
+complexes; U+02102
+cong; U+02245
+congdot; U+02A6D
+Congruent; U+02261
+Conint; U+0222F
+conint; U+0222E
+ContourIntegral; U+0222E
+Copf; U+02102
+copf; U+1D554
+coprod; U+02210
+Coproduct; U+02210
+COPY; U+000A9
+COPY U+000A9
+copy; U+000A9
+copy U+000A9
+copysr; U+02117
+CounterClockwiseContourIntegral; U+02233
+crarr; U+021B5
+Cross; U+02A2F
+cross; U+02717
+Cscr; U+1D49E
+cscr; U+1D4B8
+csub; U+02ACF
+csube; U+02AD1
+csup; U+02AD0
+csupe; U+02AD2
+ctdot; U+022EF
+cudarrl; U+02938
+cudarrr; U+02935
+cuepr; U+022DE
+cuesc; U+022DF
+cularr; U+021B6
+cularrp; U+0293D
+Cup; U+022D3
+cup; U+0222A
+cupbrcap; U+02A48
+CupCap; U+0224D
+cupcap; U+02A46
+cupcup; U+02A4A
+cupdot; U+0228D
+cupor; U+02A45
+cups; U+0222A U+0FE00
+curarr; U+021B7
+curarrm; U+0293C
+curlyeqprec; U+022DE
+curlyeqsucc; U+022DF
+curlyvee; U+022CE
+curlywedge; U+022CF
+curren; U+000A4
+curren U+000A4
+curvearrowleft; U+021B6
+curvearrowright; U+021B7
+cuvee; U+022CE
+cuwed; U+022CF
+cwconint; U+02232
+cwint; U+02231
+cylcty; U+0232D
+Dagger; U+02021
+dagger; U+02020
+daleth; U+02138
+Darr; U+021A1
+dArr; U+021D3
+darr; U+02193
+dash; U+02010
+Dashv; U+02AE4
+dashv; U+022A3
+dbkarow; U+0290F
+dblac; U+002DD
+Dcaron; U+0010E
+dcaron; U+0010F
+Dcy; U+00414
+dcy; U+00434
+DD; U+02145
+dd; U+02146
+ddagger; U+02021
+ddarr; U+021CA
+DDotrahd; U+02911
+ddotseq; U+02A77
+deg; U+000B0
+deg U+000B0
+Del; U+02207
+Delta; U+00394
+delta; U+003B4
+demptyv; U+029B1
+dfisht; U+0297F
+Dfr; U+1D507
+dfr; U+1D521
+dHar; U+02965
+dharl; U+021C3
+dharr; U+021C2
+DiacriticalAcute; U+000B4
+DiacriticalDot; U+002D9
+DiacriticalDoubleAcute; U+002DD
+DiacriticalGrave; U+00060
+DiacriticalTilde; U+002DC
+diam; U+022C4
+Diamond; U+022C4
+diamond; U+022C4
+diamondsuit; U+02666
+diams; U+02666
+die; U+000A8
+DifferentialD; U+02146
+digamma; U+003DD
+disin; U+022F2
+div; U+000F7
+divide; U+000F7
+divide U+000F7
+divideontimes; U+022C7
+divonx; U+022C7
+DJcy; U+00402
+djcy; U+00452
+dlcorn; U+0231E
+dlcrop; U+0230D
+dollar; U+00024
+Dopf; U+1D53B
+dopf; U+1D555
+Dot; U+000A8
+dot; U+002D9
+DotDot; U+020DC
+doteq; U+02250
+doteqdot; U+02251
+DotEqual; U+02250
+dotminus; U+02238
+dotplus; U+02214
+dotsquare; U+022A1
+doublebarwedge; U+02306
+DoubleContourIntegral; U+0222F
+DoubleDot; U+000A8
+DoubleDownArrow; U+021D3
+DoubleLeftArrow; U+021D0
+DoubleLeftRightArrow; U+021D4
+DoubleLeftTee; U+02AE4
+DoubleLongLeftArrow; U+027F8
+DoubleLongLeftRightArrow; U+027FA
+DoubleLongRightArrow; U+027F9
+DoubleRightArrow; U+021D2
+DoubleRightTee; U+022A8
+DoubleUpArrow; U+021D1
+DoubleUpDownArrow; U+021D5
+DoubleVerticalBar; U+02225
+DownArrow; U+02193
+Downarrow; U+021D3
+downarrow; U+02193
+DownArrowBar; U+02913
+DownArrowUpArrow; U+021F5
+DownBreve; U+00311
+downdownarrows; U+021CA
+downharpoonleft; U+021C3
+downharpoonright; U+021C2
+DownLeftRightVector; U+02950
+DownLeftTeeVector; U+0295E
+DownLeftVector; U+021BD
+DownLeftVectorBar; U+02956
+DownRightTeeVector; U+0295F
+DownRightVector; U+021C1
+DownRightVectorBar; U+02957
+DownTee; U+022A4
+DownTeeArrow; U+021A7
+drbkarow; U+02910
+drcorn; U+0231F
+drcrop; U+0230C
+Dscr; U+1D49F
+dscr; U+1D4B9
+DScy; U+00405
+dscy; U+00455
+dsol; U+029F6
+Dstrok; U+00110
+dstrok; U+00111
+dtdot; U+022F1
+dtri; U+025BF
+dtrif; U+025BE
+duarr; U+021F5
+duhar; U+0296F
+dwangle; U+029A6
+DZcy; U+0040F
+dzcy; U+0045F
+dzigrarr; U+027FF
+Eacute; U+000C9
+Eacute U+000C9
+eacute; U+000E9
+eacute U+000E9
+easter; U+02A6E
+Ecaron; U+0011A
+ecaron; U+0011B
+ecir; U+02256
+Ecirc; U+000CA
+Ecirc U+000CA
+ecirc; U+000EA
+ecirc U+000EA
+ecolon; U+02255
+Ecy; U+0042D
+ecy; U+0044D
+eDDot; U+02A77
+Edot; U+00116
+eDot; U+02251
+edot; U+00117
+ee; U+02147
+efDot; U+02252
+Efr; U+1D508
+efr; U+1D522
+eg; U+02A9A
+Egrave; U+000C8
+Egrave U+000C8
+egrave; U+000E8
+egrave U+000E8
+egs; U+02A96
+egsdot; U+02A98
+el; U+02A99
+Element; U+02208
+elinters; U+023E7
+ell; U+02113
+els; U+02A95
+elsdot; U+02A97
+Emacr; U+00112
+emacr; U+00113
+empty; U+02205
+emptyset; U+02205
+EmptySmallSquare; U+025FB
+emptyv; U+02205
+EmptyVerySmallSquare; U+025AB
+emsp; U+02003
+emsp13; U+02004
+emsp14; U+02005
+ENG; U+0014A
+eng; U+0014B
+ensp; U+02002
+Eogon; U+00118
+eogon; U+00119
+Eopf; U+1D53C
+eopf; U+1D556
+epar; U+022D5
+eparsl; U+029E3
+eplus; U+02A71
+epsi; U+003B5
+Epsilon; U+00395
+epsilon; U+003B5
+epsiv; U+003F5
+eqcirc; U+02256
+eqcolon; U+02255
+eqsim; U+02242
+eqslantgtr; U+02A96
+eqslantless; U+02A95
+Equal; U+02A75
+equals; U+0003D
+EqualTilde; U+02242
+equest; U+0225F
+Equilibrium; U+021CC
+equiv; U+02261
+equivDD; U+02A78
+eqvparsl; U+029E5
+erarr; U+02971
+erDot; U+02253
+Escr; U+02130
+escr; U+0212F
+esdot; U+02250
+Esim; U+02A73
+esim; U+02242
+Eta; U+00397
+eta; U+003B7
+ETH; U+000D0
+ETH U+000D0
+eth; U+000F0
+eth U+000F0
+Euml; U+000CB
+Euml U+000CB
+euml; U+000EB
+euml U+000EB
+euro; U+020AC
+excl; U+00021
+exist; U+02203
+Exists; U+02203
+expectation; U+02130
+ExponentialE; U+02147
+exponentiale; U+02147
+fallingdotseq; U+02252
+Fcy; U+00424
+fcy; U+00444
+female; U+02640
+ffilig; U+0FB03
+fflig; U+0FB00
+ffllig; U+0FB04
+Ffr; U+1D509
+ffr; U+1D523
+filig; U+0FB01
+FilledSmallSquare; U+025FC
+FilledVerySmallSquare; U+025AA
+fjlig; U+00066 U+0006A
+flat; U+0266D
+fllig; U+0FB02
+fltns; U+025B1
+fnof; U+00192
+Fopf; U+1D53D
+fopf; U+1D557
+ForAll; U+02200
+forall; U+02200
+fork; U+022D4
+forkv; U+02AD9
+Fouriertrf; U+02131
+fpartint; U+02A0D
+frac12; U+000BD
+frac12 U+000BD
+frac13; U+02153
+frac14; U+000BC
+frac14 U+000BC
+frac15; U+02155
+frac16; U+02159
+frac18; U+0215B
+frac23; U+02154
+frac25; U+02156
+frac34; U+000BE
+frac34 U+000BE
+frac35; U+02157
+frac38; U+0215C
+frac45; U+02158
+frac56; U+0215A
+frac58; U+0215D
+frac78; U+0215E
+frasl; U+02044
+frown; U+02322
+Fscr; U+02131
+fscr; U+1D4BB
+gacute; U+001F5
+Gamma; U+00393
+gamma; U+003B3
+Gammad; U+003DC
+gammad; U+003DD
+gap; U+02A86
+Gbreve; U+0011E
+gbreve; U+0011F
+Gcedil; U+00122
+Gcirc; U+0011C
+gcirc; U+0011D
+Gcy; U+00413
+gcy; U+00433
+Gdot; U+00120
+gdot; U+00121
+gE; U+02267
+ge; U+02265
+gEl; U+02A8C
+gel; U+022DB
+geq; U+02265
+geqq; U+02267
+geqslant; U+02A7E
+ges; U+02A7E
+gescc; U+02AA9
+gesdot; U+02A80
+gesdoto; U+02A82
+gesdotol; U+02A84
+gesl; U+022DB U+0FE00
+gesles; U+02A94
+Gfr; U+1D50A
+gfr; U+1D524
+Gg; U+022D9
+gg; U+0226B
+ggg; U+022D9
+gimel; U+02137
+GJcy; U+00403
+gjcy; U+00453
+gl; U+02277
+gla; U+02AA5
+glE; U+02A92
+glj; U+02AA4
+gnap; U+02A8A
+gnapprox; U+02A8A
+gnE; U+02269
+gne; U+02A88
+gneq; U+02A88
+gneqq; U+02269
+gnsim; U+022E7
+Gopf; U+1D53E
+gopf; U+1D558
+grave; U+00060
+GreaterEqual; U+02265
+GreaterEqualLess; U+022DB
+GreaterFullEqual; U+02267
+GreaterGreater; U+02AA2
+GreaterLess; U+02277
+GreaterSlantEqual; U+02A7E
+GreaterTilde; U+02273
+Gscr; U+1D4A2
+gscr; U+0210A
+gsim; U+02273
+gsime; U+02A8E
+gsiml; U+02A90
+GT; U+0003E
+GT U+0003E
+Gt; U+0226B
+gt; U+0003E
+gt U+0003E
+gtcc; U+02AA7
+gtcir; U+02A7A
+gtdot; U+022D7
+gtlPar; U+02995
+gtquest; U+02A7C
+gtrapprox; U+02A86
+gtrarr; U+02978
+gtrdot; U+022D7
+gtreqless; U+022DB
+gtreqqless; U+02A8C
+gtrless; U+02277
+gtrsim; U+02273
+gvertneqq; U+02269 U+0FE00
+gvnE; U+02269 U+0FE00
+Hacek; U+002C7
+hairsp; U+0200A
+half; U+000BD
+hamilt; U+0210B
+HARDcy; U+0042A
+hardcy; U+0044A
+hArr; U+021D4
+harr; U+02194
+harrcir; U+02948
+harrw; U+021AD
+Hat; U+0005E
+hbar; U+0210F
+Hcirc; U+00124
+hcirc; U+00125
+hearts; U+02665
+heartsuit; U+02665
+hellip; U+02026
+hercon; U+022B9
+Hfr; U+0210C
+hfr; U+1D525
+HilbertSpace; U+0210B
+hksearow; U+02925
+hkswarow; U+02926
+hoarr; U+021FF
+homtht; U+0223B
+hookleftarrow; U+021A9
+hookrightarrow; U+021AA
+Hopf; U+0210D
+hopf; U+1D559
+horbar; U+02015
+HorizontalLine; U+02500
+Hscr; U+0210B
+hscr; U+1D4BD
+hslash; U+0210F
+Hstrok; U+00126
+hstrok; U+00127
+HumpDownHump; U+0224E
+HumpEqual; U+0224F
+hybull; U+02043
+hyphen; U+02010
+Iacute; U+000CD
+Iacute U+000CD
+iacute; U+000ED
+iacute U+000ED
+ic; U+02063
+Icirc; U+000CE
+Icirc U+000CE
+icirc; U+000EE
+icirc U+000EE
+Icy; U+00418
+icy; U+00438
+Idot; U+00130
+IEcy; U+00415
+iecy; U+00435
+iexcl; U+000A1
+iexcl U+000A1
+iff; U+021D4
+Ifr; U+02111
+ifr; U+1D526
+Igrave; U+000CC
+Igrave U+000CC
+igrave; U+000EC
+igrave U+000EC
+ii; U+02148
+iiiint; U+02A0C
+iiint; U+0222D
+iinfin; U+029DC
+iiota; U+02129
+IJlig; U+00132
+ijlig; U+00133
+Im; U+02111
+Imacr; U+0012A
+imacr; U+0012B
+image; U+02111
+ImaginaryI; U+02148
+imagline; U+02110
+imagpart; U+02111
+imath; U+00131
+imof; U+022B7
+imped; U+001B5
+Implies; U+021D2
+in; U+02208
+incare; U+02105
+infin; U+0221E
+infintie; U+029DD
+inodot; U+00131
+Int; U+0222C
+int; U+0222B
+intcal; U+022BA
+integers; U+02124
+Integral; U+0222B
+intercal; U+022BA
+Intersection; U+022C2
+intlarhk; U+02A17
+intprod; U+02A3C
+InvisibleComma; U+02063
+InvisibleTimes; U+02062
+IOcy; U+00401
+iocy; U+00451
+Iogon; U+0012E
+iogon; U+0012F
+Iopf; U+1D540
+iopf; U+1D55A
+Iota; U+00399
+iota; U+003B9
+iprod; U+02A3C
+iquest; U+000BF
+iquest U+000BF
+Iscr; U+02110
+iscr; U+1D4BE
+isin; U+02208
+isindot; U+022F5
+isinE; U+022F9
+isins; U+022F4
+isinsv; U+022F3
+isinv; U+02208
+it; U+02062
+Itilde; U+00128
+itilde; U+00129
+Iukcy; U+00406
+iukcy; U+00456
+Iuml; U+000CF
+Iuml U+000CF
+iuml; U+000EF
+iuml U+000EF
+Jcirc; U+00134
+jcirc; U+00135
+Jcy; U+00419
+jcy; U+00439
+Jfr; U+1D50D
+jfr; U+1D527
+jmath; U+00237
+Jopf; U+1D541
+jopf; U+1D55B
+Jscr; U+1D4A5
+jscr; U+1D4BF
+Jsercy; U+00408
+jsercy; U+00458
+Jukcy; U+00404
+jukcy; U+00454
+Kappa; U+0039A
+kappa; U+003BA
+kappav; U+003F0
+Kcedil; U+00136
+kcedil; U+00137
+Kcy; U+0041A
+kcy; U+0043A
+Kfr; U+1D50E
+kfr; U+1D528
+kgreen; U+00138
+KHcy; U+00425
+khcy; U+00445
+KJcy; U+0040C
+kjcy; U+0045C
+Kopf; U+1D542
+kopf; U+1D55C
+Kscr; U+1D4A6
+kscr; U+1D4C0
+lAarr; U+021DA
+Lacute; U+00139
+lacute; U+0013A
+laemptyv; U+029B4
+lagran; U+02112
+Lambda; U+0039B
+lambda; U+003BB
+Lang; U+027EA
+lang; U+027E8
+langd; U+02991
+langle; U+027E8
+lap; U+02A85
+Laplacetrf; U+02112
+laquo; U+000AB
+laquo U+000AB
+Larr; U+0219E
+lArr; U+021D0
+larr; U+02190
+larrb; U+021E4
+larrbfs; U+0291F
+larrfs; U+0291D
+larrhk; U+021A9
+larrlp; U+021AB
+larrpl; U+02939
+larrsim; U+02973
+larrtl; U+021A2
+lat; U+02AAB
+lAtail; U+0291B
+latail; U+02919
+late; U+02AAD
+lates; U+02AAD U+0FE00
+lBarr; U+0290E
+lbarr; U+0290C
+lbbrk; U+02772
+lbrace; U+0007B
+lbrack; U+0005B
+lbrke; U+0298B
+lbrksld; U+0298F
+lbrkslu; U+0298D
+Lcaron; U+0013D
+lcaron; U+0013E
+Lcedil; U+0013B
+lcedil; U+0013C
+lceil; U+02308
+lcub; U+0007B
+Lcy; U+0041B
+lcy; U+0043B
+ldca; U+02936
+ldquo; U+0201C
+ldquor; U+0201E
+ldrdhar; U+02967
+ldrushar; U+0294B
+ldsh; U+021B2
+lE; U+02266
+le; U+02264
+LeftAngleBracket; U+027E8
+LeftArrow; U+02190
+Leftarrow; U+021D0
+leftarrow; U+02190
+LeftArrowBar; U+021E4
+LeftArrowRightArrow; U+021C6
+leftarrowtail; U+021A2
+LeftCeiling; U+02308
+LeftDoubleBracket; U+027E6
+LeftDownTeeVector; U+02961
+LeftDownVector; U+021C3
+LeftDownVectorBar; U+02959
+LeftFloor; U+0230A
+leftharpoondown; U+021BD
+leftharpoonup; U+021BC
+leftleftarrows; U+021C7
+LeftRightArrow; U+02194
+Leftrightarrow; U+021D4
+leftrightarrow; U+02194
+leftrightarrows; U+021C6
+leftrightharpoons; U+021CB
+leftrightsquigarrow; U+021AD
+LeftRightVector; U+0294E
+LeftTee; U+022A3
+LeftTeeArrow; U+021A4
+LeftTeeVector; U+0295A
+leftthreetimes; U+022CB
+LeftTriangle; U+022B2
+LeftTriangleBar; U+029CF
+LeftTriangleEqual; U+022B4
+LeftUpDownVector; U+02951
+LeftUpTeeVector; U+02960
+LeftUpVector; U+021BF
+LeftUpVectorBar; U+02958
+LeftVector; U+021BC
+LeftVectorBar; U+02952
+lEg; U+02A8B
+leg; U+022DA
+leq; U+02264
+leqq; U+02266
+leqslant; U+02A7D
+les; U+02A7D
+lescc; U+02AA8
+lesdot; U+02A7F
+lesdoto; U+02A81
+lesdotor; U+02A83
+lesg; U+022DA U+0FE00
+lesges; U+02A93
+lessapprox; U+02A85
+lessdot; U+022D6
+lesseqgtr; U+022DA
+lesseqqgtr; U+02A8B
+LessEqualGreater; U+022DA
+LessFullEqual; U+02266
+LessGreater; U+02276
+lessgtr; U+02276
+LessLess; U+02AA1
+lesssim; U+02272
+LessSlantEqual; U+02A7D
+LessTilde; U+02272
+lfisht; U+0297C
+lfloor; U+0230A
+Lfr; U+1D50F
+lfr; U+1D529
+lg; U+02276
+lgE; U+02A91
+lHar; U+02962
+lhard; U+021BD
+lharu; U+021BC
+lharul; U+0296A
+lhblk; U+02584
+LJcy; U+00409
+ljcy; U+00459
+Ll; U+022D8
+ll; U+0226A
+llarr; U+021C7
+llcorner; U+0231E
+Lleftarrow; U+021DA
+llhard; U+0296B
+lltri; U+025FA
+Lmidot; U+0013F
+lmidot; U+00140
+lmoust; U+023B0
+lmoustache; U+023B0
+lnap; U+02A89
+lnapprox; U+02A89
+lnE; U+02268
+lne; U+02A87
+lneq; U+02A87
+lneqq; U+02268
+lnsim; U+022E6
+loang; U+027EC
+loarr; U+021FD
+lobrk; U+027E6
+LongLeftArrow; U+027F5
+Longleftarrow; U+027F8
+longleftarrow; U+027F5
+LongLeftRightArrow; U+027F7
+Longleftrightarrow; U+027FA
+longleftrightarrow; U+027F7
+longmapsto; U+027FC
+LongRightArrow; U+027F6
+Longrightarrow; U+027F9
+longrightarrow; U+027F6
+looparrowleft; U+021AB
+looparrowright; U+021AC
+lopar; U+02985
+Lopf; U+1D543
+lopf; U+1D55D
+loplus; U+02A2D
+lotimes; U+02A34
+lowast; U+02217
+lowbar; U+0005F
+LowerLeftArrow; U+02199
+LowerRightArrow; U+02198
+loz; U+025CA
+lozenge; U+025CA
+lozf; U+029EB
+lpar; U+00028
+lparlt; U+02993
+lrarr; U+021C6
+lrcorner; U+0231F
+lrhar; U+021CB
+lrhard; U+0296D
+lrm; U+0200E
+lrtri; U+022BF
+lsaquo; U+02039
+Lscr; U+02112
+lscr; U+1D4C1
+Lsh; U+021B0
+lsh; U+021B0
+lsim; U+02272
+lsime; U+02A8D
+lsimg; U+02A8F
+lsqb; U+0005B
+lsquo; U+02018
+lsquor; U+0201A
+Lstrok; U+00141
+lstrok; U+00142
+LT; U+0003C
+LT U+0003C
+Lt; U+0226A
+lt; U+0003C
+lt U+0003C
+ltcc; U+02AA6
+ltcir; U+02A79
+ltdot; U+022D6
+lthree; U+022CB
+ltimes; U+022C9
+ltlarr; U+02976
+ltquest; U+02A7B
+ltri; U+025C3
+ltrie; U+022B4
+ltrif; U+025C2
+ltrPar; U+02996
+lurdshar; U+0294A
+luruhar; U+02966
+lvertneqq; U+02268 U+0FE00
+lvnE; U+02268 U+0FE00
+macr; U+000AF
+macr U+000AF
+male; U+02642
+malt; U+02720
+maltese; U+02720
+Map; U+02905
+map; U+021A6
+mapsto; U+021A6
+mapstodown; U+021A7
+mapstoleft; U+021A4
+mapstoup; U+021A5
+marker; U+025AE
+mcomma; U+02A29
+Mcy; U+0041C
+mcy; U+0043C
+mdash; U+02014
+mDDot; U+0223A
+measuredangle; U+02221
+MediumSpace; U+0205F
+Mellintrf; U+02133
+Mfr; U+1D510
+mfr; U+1D52A
+mho; U+02127
+micro; U+000B5
+micro U+000B5
+mid; U+02223
+midast; U+0002A
+midcir; U+02AF0
+middot; U+000B7
+middot U+000B7
+minus; U+02212
+minusb; U+0229F
+minusd; U+02238
+minusdu; U+02A2A
+MinusPlus; U+02213
+mlcp; U+02ADB
+mldr; U+02026
+mnplus; U+02213
+models; U+022A7
+Mopf; U+1D544
+mopf; U+1D55E
+mp; U+02213
+Mscr; U+02133
+mscr; U+1D4C2
+mstpos; U+0223E
+Mu; U+0039C
+mu; U+003BC
+multimap; U+022B8
+mumap; U+022B8
+nabla; U+02207
+Nacute; U+00143
+nacute; U+00144
+nang; U+02220 U+020D2
+nap; U+02249
+napE; U+02A70 U+00338
+napid; U+0224B U+00338
+napos; U+00149
+napprox; U+02249
+natur; U+0266E
+natural; U+0266E
+naturals; U+02115
+nbsp; U+000A0
+nbsp U+000A0
+nbump; U+0224E U+00338
+nbumpe; U+0224F U+00338
+ncap; U+02A43
+Ncaron; U+00147
+ncaron; U+00148
+Ncedil; U+00145
+ncedil; U+00146
+ncong; U+02247
+ncongdot; U+02A6D U+00338
+ncup; U+02A42
+Ncy; U+0041D
+ncy; U+0043D
+ndash; U+02013
+ne; U+02260
+nearhk; U+02924
+neArr; U+021D7
+nearr; U+02197
+nearrow; U+02197
+nedot; U+02250 U+00338
+NegativeMediumSpace; U+0200B
+NegativeThickSpace; U+0200B
+NegativeThinSpace; U+0200B
+NegativeVeryThinSpace; U+0200B
+nequiv; U+02262
+nesear; U+02928
+nesim; U+02242 U+00338
+NestedGreaterGreater; U+0226B
+NestedLessLess; U+0226A
+NewLine; U+0000A
+nexist; U+02204
+nexists; U+02204
+Nfr; U+1D511
+nfr; U+1D52B
+ngE; U+02267 U+00338
+nge; U+02271
+ngeq; U+02271
+ngeqq; U+02267 U+00338
+ngeqslant; U+02A7E U+00338
+nges; U+02A7E U+00338
+nGg; U+022D9 U+00338
+ngsim; U+02275
+nGt; U+0226B U+020D2
+ngt; U+0226F
+ngtr; U+0226F
+nGtv; U+0226B U+00338
+nhArr; U+021CE
+nharr; U+021AE
+nhpar; U+02AF2
+ni; U+0220B
+nis; U+022FC
+nisd; U+022FA
+niv; U+0220B
+NJcy; U+0040A
+njcy; U+0045A
+nlArr; U+021CD
+nlarr; U+0219A
+nldr; U+02025
+nlE; U+02266 U+00338
+nle; U+02270
+nLeftarrow; U+021CD
+nleftarrow; U+0219A
+nLeftrightarrow; U+021CE
+nleftrightarrow; U+021AE
+nleq; U+02270
+nleqq; U+02266 U+00338
+nleqslant; U+02A7D U+00338
+nles; U+02A7D U+00338
+nless; U+0226E
+nLl; U+022D8 U+00338
+nlsim; U+02274
+nLt; U+0226A U+020D2
+nlt; U+0226E
+nltri; U+022EA
+nltrie; U+022EC
+nLtv; U+0226A U+00338
+nmid; U+02224
+NoBreak; U+02060
+NonBreakingSpace; U+000A0
+Nopf; U+02115
+nopf; U+1D55F
+Not; U+02AEC
+not; U+000AC
+not U+000AC
+NotCongruent; U+02262
+NotCupCap; U+0226D
+NotDoubleVerticalBar; U+02226
+NotElement; U+02209
+NotEqual; U+02260
+NotEqualTilde; U+02242 U+00338
+NotExists; U+02204
+NotGreater; U+0226F
+NotGreaterEqual; U+02271
+NotGreaterFullEqual; U+02267 U+00338
+NotGreaterGreater; U+0226B U+00338
+NotGreaterLess; U+02279
+NotGreaterSlantEqual; U+02A7E U+00338
+NotGreaterTilde; U+02275
+NotHumpDownHump; U+0224E U+00338
+NotHumpEqual; U+0224F U+00338
+notin; U+02209
+notindot; U+022F5 U+00338
+notinE; U+022F9 U+00338
+notinva; U+02209
+notinvb; U+022F7
+notinvc; U+022F6
+NotLeftTriangle; U+022EA
+NotLeftTriangleBar; U+029CF U+00338
+NotLeftTriangleEqual; U+022EC
+NotLess; U+0226E
+NotLessEqual; U+02270
+NotLessGreater; U+02278
+NotLessLess; U+0226A U+00338
+NotLessSlantEqual; U+02A7D U+00338
+NotLessTilde; U+02274
+NotNestedGreaterGreater; U+02AA2 U+00338
+NotNestedLessLess; U+02AA1 U+00338
+notni; U+0220C
+notniva; U+0220C
+notnivb; U+022FE
+notnivc; U+022FD
+NotPrecedes; U+02280
+NotPrecedesEqual; U+02AAF U+00338
+NotPrecedesSlantEqual; U+022E0
+NotReverseElement; U+0220C
+NotRightTriangle; U+022EB
+NotRightTriangleBar; U+029D0 U+00338
+NotRightTriangleEqual; U+022ED
+NotSquareSubset; U+0228F U+00338
+NotSquareSubsetEqual; U+022E2
+NotSquareSuperset; U+02290 U+00338
+NotSquareSupersetEqual; U+022E3
+NotSubset; U+02282 U+020D2
+NotSubsetEqual; U+02288
+NotSucceeds; U+02281
+NotSucceedsEqual; U+02AB0 U+00338
+NotSucceedsSlantEqual; U+022E1
+NotSucceedsTilde; U+0227F U+00338
+NotSuperset; U+02283 U+020D2
+NotSupersetEqual; U+02289
+NotTilde; U+02241
+NotTildeEqual; U+02244
+NotTildeFullEqual; U+02247
+NotTildeTilde; U+02249
+NotVerticalBar; U+02224
+npar; U+02226
+nparallel; U+02226
+nparsl; U+02AFD U+020E5
+npart; U+02202 U+00338
+npolint; U+02A14
+npr; U+02280
+nprcue; U+022E0
+npre; U+02AAF U+00338
+nprec; U+02280
+npreceq; U+02AAF U+00338
+nrArr; U+021CF
+nrarr; U+0219B
+nrarrc; U+02933 U+00338
+nrarrw; U+0219D U+00338
+nRightarrow; U+021CF
+nrightarrow; U+0219B
+nrtri; U+022EB
+nrtrie; U+022ED
+nsc; U+02281
+nsccue; U+022E1
+nsce; U+02AB0 U+00338
+Nscr; U+1D4A9
+nscr; U+1D4C3
+nshortmid; U+02224
+nshortparallel; U+02226
+nsim; U+02241
+nsime; U+02244
+nsimeq; U+02244
+nsmid; U+02224
+nspar; U+02226
+nsqsube; U+022E2
+nsqsupe; U+022E3
+nsub; U+02284
+nsubE; U+02AC5 U+00338
+nsube; U+02288
+nsubset; U+02282 U+020D2
+nsubseteq; U+02288
+nsubseteqq; U+02AC5 U+00338
+nsucc; U+02281
+nsucceq; U+02AB0 U+00338
+nsup; U+02285
+nsupE; U+02AC6 U+00338
+nsupe; U+02289
+nsupset; U+02283 U+020D2
+nsupseteq; U+02289
+nsupseteqq; U+02AC6 U+00338
+ntgl; U+02279
+Ntilde; U+000D1
+Ntilde U+000D1
+ntilde; U+000F1
+ntilde U+000F1
+ntlg; U+02278
+ntriangleleft; U+022EA
+ntrianglelefteq; U+022EC
+ntriangleright; U+022EB
+ntrianglerighteq; U+022ED
+Nu; U+0039D
+nu; U+003BD
+num; U+00023
+numero; U+02116
+numsp; U+02007
+nvap; U+0224D U+020D2
+nVDash; U+022AF
+nVdash; U+022AE
+nvDash; U+022AD
+nvdash; U+022AC
+nvge; U+02265 U+020D2
+nvgt; U+0003E U+020D2
+nvHarr; U+02904
+nvinfin; U+029DE
+nvlArr; U+02902
+nvle; U+02264 U+020D2
+nvlt; U+0003C U+020D2
+nvltrie; U+022B4 U+020D2
+nvrArr; U+02903
+nvrtrie; U+022B5 U+020D2
+nvsim; U+0223C U+020D2
+nwarhk; U+02923
+nwArr; U+021D6
+nwarr; U+02196
+nwarrow; U+02196
+nwnear; U+02927
+Oacute; U+000D3
+Oacute U+000D3
+oacute; U+000F3
+oacute U+000F3
+oast; U+0229B
+ocir; U+0229A
+Ocirc; U+000D4
+Ocirc U+000D4
+ocirc; U+000F4
+ocirc U+000F4
+Ocy; U+0041E
+ocy; U+0043E
+odash; U+0229D
+Odblac; U+00150
+odblac; U+00151
+odiv; U+02A38
+odot; U+02299
+odsold; U+029BC
+OElig; U+00152
+oelig; U+00153
+ofcir; U+029BF
+Ofr; U+1D512
+ofr; U+1D52C
+ogon; U+002DB
+Ograve; U+000D2
+Ograve U+000D2
+ograve; U+000F2
+ograve U+000F2
+ogt; U+029C1
+ohbar; U+029B5
+ohm; U+003A9
+oint; U+0222E
+olarr; U+021BA
+olcir; U+029BE
+olcross; U+029BB
+oline; U+0203E
+olt; U+029C0
+Omacr; U+0014C
+omacr; U+0014D
+Omega; U+003A9
+omega; U+003C9
+Omicron; U+0039F
+omicron; U+003BF
+omid; U+029B6
+ominus; U+02296
+Oopf; U+1D546
+oopf; U+1D560
+opar; U+029B7
+OpenCurlyDoubleQuote; U+0201C
+OpenCurlyQuote; U+02018
+operp; U+029B9
+oplus; U+02295
+Or; U+02A54
+or; U+02228
+orarr; U+021BB
+ord; U+02A5D
+order; U+02134
+orderof; U+02134
+ordf; U+000AA
+ordf U+000AA
+ordm; U+000BA
+ordm U+000BA
+origof; U+022B6
+oror; U+02A56
+orslope; U+02A57
+orv; U+02A5B
+oS; U+024C8
+Oscr; U+1D4AA
+oscr; U+02134
+Oslash; U+000D8
+Oslash U+000D8
+oslash; U+000F8
+oslash U+000F8
+osol; U+02298
+Otilde; U+000D5
+Otilde U+000D5
+otilde; U+000F5
+otilde U+000F5
+Otimes; U+02A37
+otimes; U+02297
+otimesas; U+02A36
+Ouml; U+000D6
+Ouml U+000D6
+ouml; U+000F6
+ouml U+000F6
+ovbar; U+0233D
+OverBar; U+0203E
+OverBrace; U+023DE
+OverBracket; U+023B4
+OverParenthesis; U+023DC
+par; U+02225
+para; U+000B6
+para U+000B6
+parallel; U+02225
+parsim; U+02AF3
+parsl; U+02AFD
+part; U+02202
+PartialD; U+02202
+Pcy; U+0041F
+pcy; U+0043F
+percnt; U+00025
+period; U+0002E
+permil; U+02030
+perp; U+022A5
+pertenk; U+02031
+Pfr; U+1D513
+pfr; U+1D52D
+Phi; U+003A6
+phi; U+003C6
+phiv; U+003D5
+phmmat; U+02133
+phone; U+0260E
+Pi; U+003A0
+pi; U+003C0
+pitchfork; U+022D4
+piv; U+003D6
+planck; U+0210F
+planckh; U+0210E
+plankv; U+0210F
+plus; U+0002B
+plusacir; U+02A23
+plusb; U+0229E
+pluscir; U+02A22
+plusdo; U+02214
+plusdu; U+02A25
+pluse; U+02A72
+PlusMinus; U+000B1
+plusmn; U+000B1
+plusmn U+000B1
+plussim; U+02A26
+plustwo; U+02A27
+pm; U+000B1
+Poincareplane; U+0210C
+pointint; U+02A15
+Popf; U+02119
+popf; U+1D561
+pound; U+000A3
+pound U+000A3
+Pr; U+02ABB
+pr; U+0227A
+prap; U+02AB7
+prcue; U+0227C
+prE; U+02AB3
+pre; U+02AAF
+prec; U+0227A
+precapprox; U+02AB7
+preccurlyeq; U+0227C
+Precedes; U+0227A
+PrecedesEqual; U+02AAF
+PrecedesSlantEqual; U+0227C
+PrecedesTilde; U+0227E
+preceq; U+02AAF
+precnapprox; U+02AB9
+precneqq; U+02AB5
+precnsim; U+022E8
+precsim; U+0227E
+Prime; U+02033
+prime; U+02032
+primes; U+02119
+prnap; U+02AB9
+prnE; U+02AB5
+prnsim; U+022E8
+prod; U+0220F
+Product; U+0220F
+profalar; U+0232E
+profline; U+02312
+profsurf; U+02313
+prop; U+0221D
+Proportion; U+02237
+Proportional; U+0221D
+propto; U+0221D
+prsim; U+0227E
+prurel; U+022B0
+Pscr; U+1D4AB
+pscr; U+1D4C5
+Psi; U+003A8
+psi; U+003C8
+puncsp; U+02008
+Qfr; U+1D514
+qfr; U+1D52E
+qint; U+02A0C
+Qopf; U+0211A
+qopf; U+1D562
+qprime; U+02057
+Qscr; U+1D4AC
+qscr; U+1D4C6
+quaternions; U+0210D
+quatint; U+02A16
+quest; U+0003F
+questeq; U+0225F
+QUOT; U+00022
+QUOT U+00022
+quot; U+00022
+quot U+00022
+rAarr; U+021DB
+race; U+0223D U+00331
+Racute; U+00154
+racute; U+00155
+radic; U+0221A
+raemptyv; U+029B3
+Rang; U+027EB
+rang; U+027E9
+rangd; U+02992
+range; U+029A5
+rangle; U+027E9
+raquo; U+000BB
+raquo U+000BB
+Rarr; U+021A0
+rArr; U+021D2
+rarr; U+02192
+rarrap; U+02975
+rarrb; U+021E5
+rarrbfs; U+02920
+rarrc; U+02933
+rarrfs; U+0291E
+rarrhk; U+021AA
+rarrlp; U+021AC
+rarrpl; U+02945
+rarrsim; U+02974
+Rarrtl; U+02916
+rarrtl; U+021A3
+rarrw; U+0219D
+rAtail; U+0291C
+ratail; U+0291A
+ratio; U+02236
+rationals; U+0211A
+RBarr; U+02910
+rBarr; U+0290F
+rbarr; U+0290D
+rbbrk; U+02773
+rbrace; U+0007D
+rbrack; U+0005D
+rbrke; U+0298C
+rbrksld; U+0298E
+rbrkslu; U+02990
+Rcaron; U+00158
+rcaron; U+00159
+Rcedil; U+00156
+rcedil; U+00157
+rceil; U+02309
+rcub; U+0007D
+Rcy; U+00420
+rcy; U+00440
+rdca; U+02937
+rdldhar; U+02969
+rdquo; U+0201D
+rdquor; U+0201D
+rdsh; U+021B3
+Re; U+0211C
+real; U+0211C
+realine; U+0211B
+realpart; U+0211C
+reals; U+0211D
+rect; U+025AD
+REG; U+000AE
+REG U+000AE
+reg; U+000AE
+reg U+000AE
+ReverseElement; U+0220B
+ReverseEquilibrium; U+021CB
+ReverseUpEquilibrium; U+0296F
+rfisht; U+0297D
+rfloor; U+0230B
+Rfr; U+0211C
+rfr; U+1D52F
+rHar; U+02964
+rhard; U+021C1
+rharu; U+021C0
+rharul; U+0296C
+Rho; U+003A1
+rho; U+003C1
+rhov; U+003F1
+RightAngleBracket; U+027E9
+RightArrow; U+02192
+Rightarrow; U+021D2
+rightarrow; U+02192
+RightArrowBar; U+021E5
+RightArrowLeftArrow; U+021C4
+rightarrowtail; U+021A3
+RightCeiling; U+02309
+RightDoubleBracket; U+027E7
+RightDownTeeVector; U+0295D
+RightDownVector; U+021C2
+RightDownVectorBar; U+02955
+RightFloor; U+0230B
+rightharpoondown; U+021C1
+rightharpoonup; U+021C0
+rightleftarrows; U+021C4
+rightleftharpoons; U+021CC
+rightrightarrows; U+021C9
+rightsquigarrow; U+0219D
+RightTee; U+022A2
+RightTeeArrow; U+021A6
+RightTeeVector; U+0295B
+rightthreetimes; U+022CC
+RightTriangle; U+022B3
+RightTriangleBar; U+029D0
+RightTriangleEqual; U+022B5
+RightUpDownVector; U+0294F
+RightUpTeeVector; U+0295C
+RightUpVector; U+021BE
+RightUpVectorBar; U+02954
+RightVector; U+021C0
+RightVectorBar; U+02953
+ring; U+002DA
+risingdotseq; U+02253
+rlarr; U+021C4
+rlhar; U+021CC
+rlm; U+0200F
+rmoust; U+023B1
+rmoustache; U+023B1
+rnmid; U+02AEE
+roang; U+027ED
+roarr; U+021FE
+robrk; U+027E7
+ropar; U+02986
+Ropf; U+0211D
+ropf; U+1D563
+roplus; U+02A2E
+rotimes; U+02A35
+RoundImplies; U+02970
+rpar; U+00029
+rpargt; U+02994
+rppolint; U+02A12
+rrarr; U+021C9
+Rrightarrow; U+021DB
+rsaquo; U+0203A
+Rscr; U+0211B
+rscr; U+1D4C7
+Rsh; U+021B1
+rsh; U+021B1
+rsqb; U+0005D
+rsquo; U+02019
+rsquor; U+02019
+rthree; U+022CC
+rtimes; U+022CA
+rtri; U+025B9
+rtrie; U+022B5
+rtrif; U+025B8
+rtriltri; U+029CE
+RuleDelayed; U+029F4
+ruluhar; U+02968
+rx; U+0211E
+Sacute; U+0015A
+sacute; U+0015B
+sbquo; U+0201A
+Sc; U+02ABC
+sc; U+0227B
+scap; U+02AB8
+Scaron; U+00160
+scaron; U+00161
+sccue; U+0227D
+scE; U+02AB4
+sce; U+02AB0
+Scedil; U+0015E
+scedil; U+0015F
+Scirc; U+0015C
+scirc; U+0015D
+scnap; U+02ABA
+scnE; U+02AB6
+scnsim; U+022E9
+scpolint; U+02A13
+scsim; U+0227F
+Scy; U+00421
+scy; U+00441
+sdot; U+022C5
+sdotb; U+022A1
+sdote; U+02A66
+searhk; U+02925
+seArr; U+021D8
+searr; U+02198
+searrow; U+02198
+sect; U+000A7
+sect U+000A7
+semi; U+0003B
+seswar; U+02929
+setminus; U+02216
+setmn; U+02216
+sext; U+02736
+Sfr; U+1D516
+sfr; U+1D530
+sfrown; U+02322
+sharp; U+0266F
+SHCHcy; U+00429
+shchcy; U+00449
+SHcy; U+00428
+shcy; U+00448
+ShortDownArrow; U+02193
+ShortLeftArrow; U+02190
+shortmid; U+02223
+shortparallel; U+02225
+ShortRightArrow; U+02192
+ShortUpArrow; U+02191
+shy; U+000AD
+shy U+000AD
+Sigma; U+003A3
+sigma; U+003C3
+sigmaf; U+003C2
+sigmav; U+003C2
+sim; U+0223C
+simdot; U+02A6A
+sime; U+02243
+simeq; U+02243
+simg; U+02A9E
+simgE; U+02AA0
+siml; U+02A9D
+simlE; U+02A9F
+simne; U+02246
+simplus; U+02A24
+simrarr; U+02972
+slarr; U+02190
+SmallCircle; U+02218
+smallsetminus; U+02216
+smashp; U+02A33
+smeparsl; U+029E4
+smid; U+02223
+smile; U+02323
+smt; U+02AAA
+smte; U+02AAC
+smtes; U+02AAC U+0FE00
+SOFTcy; U+0042C
+softcy; U+0044C
+sol; U+0002F
+solb; U+029C4
+solbar; U+0233F
+Sopf; U+1D54A
+sopf; U+1D564
+spades; U+02660
+spadesuit; U+02660
+spar; U+02225
+sqcap; U+02293
+sqcaps; U+02293 U+0FE00
+sqcup; U+02294
+sqcups; U+02294 U+0FE00
+Sqrt; U+0221A
+sqsub; U+0228F
+sqsube; U+02291
+sqsubset; U+0228F
+sqsubseteq; U+02291
+sqsup; U+02290
+sqsupe; U+02292
+sqsupset; U+02290
+sqsupseteq; U+02292
+squ; U+025A1
+Square; U+025A1
+square; U+025A1
+SquareIntersection; U+02293
+SquareSubset; U+0228F
+SquareSubsetEqual; U+02291
+SquareSuperset; U+02290
+SquareSupersetEqual; U+02292
+SquareUnion; U+02294
+squarf; U+025AA
+squf; U+025AA
+srarr; U+02192
+Sscr; U+1D4AE
+sscr; U+1D4C8
+ssetmn; U+02216
+ssmile; U+02323
+sstarf; U+022C6
+Star; U+022C6
+star; U+02606
+starf; U+02605
+straightepsilon; U+003F5
+straightphi; U+003D5
+strns; U+000AF
+Sub; U+022D0
+sub; U+02282
+subdot; U+02ABD
+subE; U+02AC5
+sube; U+02286
+subedot; U+02AC3
+submult; U+02AC1
+subnE; U+02ACB
+subne; U+0228A
+subplus; U+02ABF
+subrarr; U+02979
+Subset; U+022D0
+subset; U+02282
+subseteq; U+02286
+subseteqq; U+02AC5
+SubsetEqual; U+02286
+subsetneq; U+0228A
+subsetneqq; U+02ACB
+subsim; U+02AC7
+subsub; U+02AD5
+subsup; U+02AD3
+succ; U+0227B
+succapprox; U+02AB8
+succcurlyeq; U+0227D
+Succeeds; U+0227B
+SucceedsEqual; U+02AB0
+SucceedsSlantEqual; U+0227D
+SucceedsTilde; U+0227F
+succeq; U+02AB0
+succnapprox; U+02ABA
+succneqq; U+02AB6
+succnsim; U+022E9
+succsim; U+0227F
+SuchThat; U+0220B
+Sum; U+02211
+sum; U+02211
+sung; U+0266A
+Sup; U+022D1
+sup; U+02283
+sup1; U+000B9
+sup1 U+000B9
+sup2; U+000B2
+sup2 U+000B2
+sup3; U+000B3
+sup3 U+000B3
+supdot; U+02ABE
+supdsub; U+02AD8
+supE; U+02AC6
+supe; U+02287
+supedot; U+02AC4
+Superset; U+02283
+SupersetEqual; U+02287
+suphsol; U+027C9
+suphsub; U+02AD7
+suplarr; U+0297B
+supmult; U+02AC2
+supnE; U+02ACC
+supne; U+0228B
+supplus; U+02AC0
+Supset; U+022D1
+supset; U+02283
+supseteq; U+02287
+supseteqq; U+02AC6
+supsetneq; U+0228B
+supsetneqq; U+02ACC
+supsim; U+02AC8
+supsub; U+02AD4
+supsup; U+02AD6
+swarhk; U+02926
+swArr; U+021D9
+swarr; U+02199
+swarrow; U+02199
+swnwar; U+0292A
+szlig; U+000DF
+szlig U+000DF
+Tab; U+00009
+target; U+02316
+Tau; U+003A4
+tau; U+003C4
+tbrk; U+023B4
+Tcaron; U+00164
+tcaron; U+00165
+Tcedil; U+00162
+tcedil; U+00163
+Tcy; U+00422
+tcy; U+00442
+tdot; U+020DB
+telrec; U+02315
+Tfr; U+1D517
+tfr; U+1D531
+there4; U+02234
+Therefore; U+02234
+therefore; U+02234
+Theta; U+00398
+theta; U+003B8
+thetasym; U+003D1
+thetav; U+003D1
+thickapprox; U+02248
+thicksim; U+0223C
+ThickSpace; U+0205F U+0200A
+thinsp; U+02009
+ThinSpace; U+02009
+thkap; U+02248
+thksim; U+0223C
+THORN; U+000DE
+THORN U+000DE
+thorn; U+000FE
+thorn U+000FE
+Tilde; U+0223C
+tilde; U+002DC
+TildeEqual; U+02243
+TildeFullEqual; U+02245
+TildeTilde; U+02248
+times; U+000D7
+times U+000D7
+timesb; U+022A0
+timesbar; U+02A31
+timesd; U+02A30
+tint; U+0222D
+toea; U+02928
+top; U+022A4
+topbot; U+02336
+topcir; U+02AF1
+Topf; U+1D54B
+topf; U+1D565
+topfork; U+02ADA
+tosa; U+02929
+tprime; U+02034
+TRADE; U+02122
+trade; U+02122
+triangle; U+025B5
+triangledown; U+025BF
+triangleleft; U+025C3
+trianglelefteq; U+022B4
+triangleq; U+0225C
+triangleright; U+025B9
+trianglerighteq; U+022B5
+tridot; U+025EC
+trie; U+0225C
+triminus; U+02A3A
+TripleDot; U+020DB
+triplus; U+02A39
+trisb; U+029CD
+tritime; U+02A3B
+trpezium; U+023E2
+Tscr; U+1D4AF
+tscr; U+1D4C9
+TScy; U+00426
+tscy; U+00446
+TSHcy; U+0040B
+tshcy; U+0045B
+Tstrok; U+00166
+tstrok; U+00167
+twixt; U+0226C
+twoheadleftarrow; U+0219E
+twoheadrightarrow; U+021A0
+Uacute; U+000DA
+Uacute U+000DA
+uacute; U+000FA
+uacute U+000FA
+Uarr; U+0219F
+uArr; U+021D1
+uarr; U+02191
+Uarrocir; U+02949
+Ubrcy; U+0040E
+ubrcy; U+0045E
+Ubreve; U+0016C
+ubreve; U+0016D
+Ucirc; U+000DB
+Ucirc U+000DB
+ucirc; U+000FB
+ucirc U+000FB
+Ucy; U+00423
+ucy; U+00443
+udarr; U+021C5
+Udblac; U+00170
+udblac; U+00171
+udhar; U+0296E
+ufisht; U+0297E
+Ufr; U+1D518
+ufr; U+1D532
+Ugrave; U+000D9
+Ugrave U+000D9
+ugrave; U+000F9
+ugrave U+000F9
+uHar; U+02963
+uharl; U+021BF
+uharr; U+021BE
+uhblk; U+02580
+ulcorn; U+0231C
+ulcorner; U+0231C
+ulcrop; U+0230F
+ultri; U+025F8
+Umacr; U+0016A
+umacr; U+0016B
+uml; U+000A8
+uml U+000A8
+UnderBar; U+0005F
+UnderBrace; U+023DF
+UnderBracket; U+023B5
+UnderParenthesis; U+023DD
+Union; U+022C3
+UnionPlus; U+0228E
+Uogon; U+00172
+uogon; U+00173
+Uopf; U+1D54C
+uopf; U+1D566
+UpArrow; U+02191
+Uparrow; U+021D1
+uparrow; U+02191
+UpArrowBar; U+02912
+UpArrowDownArrow; U+021C5
+UpDownArrow; U+02195
+Updownarrow; U+021D5
+updownarrow; U+02195
+UpEquilibrium; U+0296E
+upharpoonleft; U+021BF
+upharpoonright; U+021BE
+uplus; U+0228E
+UpperLeftArrow; U+02196
+UpperRightArrow; U+02197
+Upsi; U+003D2
+upsi; U+003C5
+upsih; U+003D2
+Upsilon; U+003A5
+upsilon; U+003C5
+UpTee; U+022A5
+UpTeeArrow; U+021A5
+upuparrows; U+021C8
+urcorn; U+0231D
+urcorner; U+0231D
+urcrop; U+0230E
+Uring; U+0016E
+uring; U+0016F
+urtri; U+025F9
+Uscr; U+1D4B0
+uscr; U+1D4CA
+utdot; U+022F0
+Utilde; U+00168
+utilde; U+00169
+utri; U+025B5
+utrif; U+025B4
+uuarr; U+021C8
+Uuml; U+000DC
+Uuml U+000DC
+uuml; U+000FC
+uuml U+000FC
+uwangle; U+029A7
+vangrt; U+0299C
+varepsilon; U+003F5
+varkappa; U+003F0
+varnothing; U+02205
+varphi; U+003D5
+varpi; U+003D6
+varpropto; U+0221D
+vArr; U+021D5
+varr; U+02195
+varrho; U+003F1
+varsigma; U+003C2
+varsubsetneq; U+0228A U+0FE00
+varsubsetneqq; U+02ACB U+0FE00
+varsupsetneq; U+0228B U+0FE00
+varsupsetneqq; U+02ACC U+0FE00
+vartheta; U+003D1
+vartriangleleft; U+022B2
+vartriangleright; U+022B3
+Vbar; U+02AEB
+vBar; U+02AE8
+vBarv; U+02AE9
+Vcy; U+00412
+vcy; U+00432
+VDash; U+022AB
+Vdash; U+022A9
+vDash; U+022A8
+vdash; U+022A2
+Vdashl; U+02AE6
+Vee; U+022C1
+vee; U+02228
+veebar; U+022BB
+veeeq; U+0225A
+vellip; U+022EE
+Verbar; U+02016
+verbar; U+0007C
+Vert; U+02016
+vert; U+0007C
+VerticalBar; U+02223
+VerticalLine; U+0007C
+VerticalSeparator; U+02758
+VerticalTilde; U+02240
+VeryThinSpace; U+0200A
+Vfr; U+1D519
+vfr; U+1D533
+vltri; U+022B2
+vnsub; U+02282 U+020D2
+vnsup; U+02283 U+020D2
+Vopf; U+1D54D
+vopf; U+1D567
+vprop; U+0221D
+vrtri; U+022B3
+Vscr; U+1D4B1
+vscr; U+1D4CB
+vsubnE; U+02ACB U+0FE00
+vsubne; U+0228A U+0FE00
+vsupnE; U+02ACC U+0FE00
+vsupne; U+0228B U+0FE00
+Vvdash; U+022AA
+vzigzag; U+0299A
+Wcirc; U+00174
+wcirc; U+00175
+wedbar; U+02A5F
+Wedge; U+022C0
+wedge; U+02227
+wedgeq; U+02259
+weierp; U+02118
+Wfr; U+1D51A
+wfr; U+1D534
+Wopf; U+1D54E
+wopf; U+1D568
+wp; U+02118
+wr; U+02240
+wreath; U+02240
+Wscr; U+1D4B2
+wscr; U+1D4CC
+xcap; U+022C2
+xcirc; U+025EF
+xcup; U+022C3
+xdtri; U+025BD
+Xfr; U+1D51B
+xfr; U+1D535
+xhArr; U+027FA
+xharr; U+027F7
+Xi; U+0039E
+xi; U+003BE
+xlArr; U+027F8
+xlarr; U+027F5
+xmap; U+027FC
+xnis; U+022FB
+xodot; U+02A00
+Xopf; U+1D54F
+xopf; U+1D569
+xoplus; U+02A01
+xotime; U+02A02
+xrArr; U+027F9
+xrarr; U+027F6
+Xscr; U+1D4B3
+xscr; U+1D4CD
+xsqcup; U+02A06
+xuplus; U+02A04
+xutri; U+025B3
+xvee; U+022C1
+xwedge; U+022C0
+Yacute; U+000DD
+Yacute U+000DD
+yacute; U+000FD
+yacute U+000FD
+YAcy; U+0042F
+yacy; U+0044F
+Ycirc; U+00176
+ycirc; U+00177
+Ycy; U+0042B
+ycy; U+0044B
+yen; U+000A5
+yen U+000A5
+Yfr; U+1D51C
+yfr; U+1D536
+YIcy; U+00407
+yicy; U+00457
+Yopf; U+1D550
+yopf; U+1D56A
+Yscr; U+1D4B4
+yscr; U+1D4CE
+YUcy; U+0042E
+yucy; U+0044E
+Yuml; U+00178
+yuml; U+000FF
+yuml U+000FF
+Zacute; U+00179
+zacute; U+0017A
+Zcaron; U+0017D
+zcaron; U+0017E
+Zcy; U+00417
+zcy; U+00437
+Zdot; U+0017B
+zdot; U+0017C
+zeetrf; U+02128
+ZeroWidthSpace; U+0200B
+Zeta; U+00396
+zeta; U+003B6
+Zfr; U+02128
+zfr; U+1D537
+ZHcy; U+00416
+zhcy; U+00436
+zigrarr; U+021DD
+Zopf; U+02124
+zopf; U+1D56B
+Zscr; U+1D4B5
+zscr; U+1D4CF
+zwj; U+0200D
+zwnj; U+0200C
@@ -1,2231 +0,0 @@
-Aacute; U+000C1
-Aacute U+000C1
-aacute; U+000E1
-aacute U+000E1
-Abreve; U+00102
-abreve; U+00103
-ac; U+0223E
-acd; U+0223F
-acE; U+0223E U+00333
-Acirc; U+000C2
-Acirc U+000C2
-acirc; U+000E2
-acirc U+000E2
-acute; U+000B4
-acute U+000B4
-Acy; U+00410
-acy; U+00430
-AElig; U+000C6
-AElig U+000C6
-aelig; U+000E6
-aelig U+000E6
-af; U+02061
-Afr; U+1D504
-afr; U+1D51E
-Agrave; U+000C0
-Agrave U+000C0
-agrave; U+000E0
-agrave U+000E0
-alefsym; U+02135
-aleph; U+02135
-Alpha; U+00391
-alpha; U+003B1
-Amacr; U+00100
-amacr; U+00101
-amalg; U+02A3F
-AMP; U+00026
-AMP U+00026
-amp; U+00026
-amp U+00026
-And; U+02A53
-and; U+02227
-andand; U+02A55
-andd; U+02A5C
-andslope; U+02A58
-andv; U+02A5A
-ang; U+02220
-ange; U+029A4
-angle; U+02220
-angmsd; U+02221
-angmsdaa; U+029A8
-angmsdab; U+029A9
-angmsdac; U+029AA
-angmsdad; U+029AB
-angmsdae; U+029AC
-angmsdaf; U+029AD
-angmsdag; U+029AE
-angmsdah; U+029AF
-angrt; U+0221F
-angrtvb; U+022BE
-angrtvbd; U+0299D
-angsph; U+02222
-angst; U+000C5
-angzarr; U+0237C
-Aogon; U+00104
-aogon; U+00105
-Aopf; U+1D538
-aopf; U+1D552
-ap; U+02248
-apacir; U+02A6F
-apE; U+02A70
-ape; U+0224A
-apid; U+0224B
-apos; U+00027
-ApplyFunction; U+02061
-approx; U+02248
-approxeq; U+0224A
-Aring; U+000C5
-Aring U+000C5
-aring; U+000E5
-aring U+000E5
-Ascr; U+1D49C
-ascr; U+1D4B6
-Assign; U+02254
-ast; U+0002A
-asymp; U+02248
-asympeq; U+0224D
-Atilde; U+000C3
-Atilde U+000C3
-atilde; U+000E3
-atilde U+000E3
-Auml; U+000C4
-Auml U+000C4
-auml; U+000E4
-auml U+000E4
-awconint; U+02233
-awint; U+02A11
-backcong; U+0224C
-backepsilon; U+003F6
-backprime; U+02035
-backsim; U+0223D
-backsimeq; U+022CD
-Backslash; U+02216
-Barv; U+02AE7
-barvee; U+022BD
-Barwed; U+02306
-barwed; U+02305
-barwedge; U+02305
-bbrk; U+023B5
-bbrktbrk; U+023B6
-bcong; U+0224C
-Bcy; U+00411
-bcy; U+00431
-bdquo; U+0201E
-becaus; U+02235
-Because; U+02235
-because; U+02235
-bemptyv; U+029B0
-bepsi; U+003F6
-bernou; U+0212C
-Bernoullis; U+0212C
-Beta; U+00392
-beta; U+003B2
-beth; U+02136
-between; U+0226C
-Bfr; U+1D505
-bfr; U+1D51F
-bigcap; U+022C2
-bigcirc; U+025EF
-bigcup; U+022C3
-bigodot; U+02A00
-bigoplus; U+02A01
-bigotimes; U+02A02
-bigsqcup; U+02A06
-bigstar; U+02605
-bigtriangledown; U+025BD
-bigtriangleup; U+025B3
-biguplus; U+02A04
-bigvee; U+022C1
-bigwedge; U+022C0
-bkarow; U+0290D
-blacklozenge; U+029EB
-blacksquare; U+025AA
-blacktriangle; U+025B4
-blacktriangledown; U+025BE
-blacktriangleleft; U+025C2
-blacktriangleright; U+025B8
-blank; U+02423
-blk12; U+02592
-blk14; U+02591
-blk34; U+02593
-block; U+02588
-bne; U+0003D U+020E5
-bnequiv; U+02261 U+020E5
-bNot; U+02AED
-bnot; U+02310
-Bopf; U+1D539
-bopf; U+1D553
-bot; U+022A5
-bottom; U+022A5
-bowtie; U+022C8
-boxbox; U+029C9
-boxDL; U+02557
-boxDl; U+02556
-boxdL; U+02555
-boxdl; U+02510
-boxDR; U+02554
-boxDr; U+02553
-boxdR; U+02552
-boxdr; U+0250C
-boxH; U+02550
-boxh; U+02500
-boxHD; U+02566
-boxHd; U+02564
-boxhD; U+02565
-boxhd; U+0252C
-boxHU; U+02569
-boxHu; U+02567
-boxhU; U+02568
-boxhu; U+02534
-boxminus; U+0229F
-boxplus; U+0229E
-boxtimes; U+022A0
-boxUL; U+0255D
-boxUl; U+0255C
-boxuL; U+0255B
-boxul; U+02518
-boxUR; U+0255A
-boxUr; U+02559
-boxuR; U+02558
-boxur; U+02514
-boxV; U+02551
-boxv; U+02502
-boxVH; U+0256C
-boxVh; U+0256B
-boxvH; U+0256A
-boxvh; U+0253C
-boxVL; U+02563
-boxVl; U+02562
-boxvL; U+02561
-boxvl; U+02524
-boxVR; U+02560
-boxVr; U+0255F
-boxvR; U+0255E
-boxvr; U+0251C
-bprime; U+02035
-Breve; U+002D8
-breve; U+002D8
-brvbar; U+000A6
-brvbar U+000A6
-Bscr; U+0212C
-bscr; U+1D4B7
-bsemi; U+0204F
-bsim; U+0223D
-bsime; U+022CD
-bsol; U+0005C
-bsolb; U+029C5
-bsolhsub; U+027C8
-bull; U+02022
-bullet; U+02022
-bump; U+0224E
-bumpE; U+02AAE
-bumpe; U+0224F
-Bumpeq; U+0224E
-bumpeq; U+0224F
-Cacute; U+00106
-cacute; U+00107
-Cap; U+022D2
-cap; U+02229
-capand; U+02A44
-capbrcup; U+02A49
-capcap; U+02A4B
-capcup; U+02A47
-capdot; U+02A40
-CapitalDifferentialD; U+02145
-caps; U+02229 U+0FE00
-caret; U+02041
-caron; U+002C7
-Cayleys; U+0212D
-ccaps; U+02A4D
-Ccaron; U+0010C
-ccaron; U+0010D
-Ccedil; U+000C7
-Ccedil U+000C7
-ccedil; U+000E7
-ccedil U+000E7
-Ccirc; U+00108
-ccirc; U+00109
-Cconint; U+02230
-ccups; U+02A4C
-ccupssm; U+02A50
-Cdot; U+0010A
-cdot; U+0010B
-cedil; U+000B8
-cedil U+000B8
-Cedilla; U+000B8
-cemptyv; U+029B2
-cent; U+000A2
-cent U+000A2
-CenterDot; U+000B7
-centerdot; U+000B7
-Cfr; U+0212D
-cfr; U+1D520
-CHcy; U+00427
-chcy; U+00447
-check; U+02713
-checkmark; U+02713
-Chi; U+003A7
-chi; U+003C7
-cir; U+025CB
-circ; U+002C6
-circeq; U+02257
-circlearrowleft; U+021BA
-circlearrowright; U+021BB
-circledast; U+0229B
-circledcirc; U+0229A
-circleddash; U+0229D
-CircleDot; U+02299
-circledR; U+000AE
-circledS; U+024C8
-CircleMinus; U+02296
-CirclePlus; U+02295
-CircleTimes; U+02297
-cirE; U+029C3
-cire; U+02257
-cirfnint; U+02A10
-cirmid; U+02AEF
-cirscir; U+029C2
-ClockwiseContourIntegral; U+02232
-CloseCurlyDoubleQuote; U+0201D
-CloseCurlyQuote; U+02019
-clubs; U+02663
-clubsuit; U+02663
-Colon; U+02237
-colon; U+0003A
-Colone; U+02A74
-colone; U+02254
-coloneq; U+02254
-comma; U+0002C
-commat; U+00040
-comp; U+02201
-compfn; U+02218
-complement; U+02201
-complexes; U+02102
-cong; U+02245
-congdot; U+02A6D
-Congruent; U+02261
-Conint; U+0222F
-conint; U+0222E
-ContourIntegral; U+0222E
-Copf; U+02102
-copf; U+1D554
-coprod; U+02210
-Coproduct; U+02210
-COPY; U+000A9
-COPY U+000A9
-copy; U+000A9
-copy U+000A9
-copysr; U+02117
-CounterClockwiseContourIntegral; U+02233
-crarr; U+021B5
-Cross; U+02A2F
-cross; U+02717
-Cscr; U+1D49E
-cscr; U+1D4B8
-csub; U+02ACF
-csube; U+02AD1
-csup; U+02AD0
-csupe; U+02AD2
-ctdot; U+022EF
-cudarrl; U+02938
-cudarrr; U+02935
-cuepr; U+022DE
-cuesc; U+022DF
-cularr; U+021B6
-cularrp; U+0293D
-Cup; U+022D3
-cup; U+0222A
-cupbrcap; U+02A48
-CupCap; U+0224D
-cupcap; U+02A46
-cupcup; U+02A4A
-cupdot; U+0228D
-cupor; U+02A45
-cups; U+0222A U+0FE00
-curarr; U+021B7
-curarrm; U+0293C
-curlyeqprec; U+022DE
-curlyeqsucc; U+022DF
-curlyvee; U+022CE
-curlywedge; U+022CF
-curren; U+000A4
-curren U+000A4
-curvearrowleft; U+021B6
-curvearrowright; U+021B7
-cuvee; U+022CE
-cuwed; U+022CF
-cwconint; U+02232
-cwint; U+02231
-cylcty; U+0232D
-Dagger; U+02021
-dagger; U+02020
-daleth; U+02138
-Darr; U+021A1
-dArr; U+021D3
-darr; U+02193
-dash; U+02010
-Dashv; U+02AE4
-dashv; U+022A3
-dbkarow; U+0290F
-dblac; U+002DD
-Dcaron; U+0010E
-dcaron; U+0010F
-Dcy; U+00414
-dcy; U+00434
-DD; U+02145
-dd; U+02146
-ddagger; U+02021
-ddarr; U+021CA
-DDotrahd; U+02911
-ddotseq; U+02A77
-deg; U+000B0
-deg U+000B0
-Del; U+02207
-Delta; U+00394
-delta; U+003B4
-demptyv; U+029B1
-dfisht; U+0297F
-Dfr; U+1D507
-dfr; U+1D521
-dHar; U+02965
-dharl; U+021C3
-dharr; U+021C2
-DiacriticalAcute; U+000B4
-DiacriticalDot; U+002D9
-DiacriticalDoubleAcute; U+002DD
-DiacriticalGrave; U+00060
-DiacriticalTilde; U+002DC
-diam; U+022C4
-Diamond; U+022C4
-diamond; U+022C4
-diamondsuit; U+02666
-diams; U+02666
-die; U+000A8
-DifferentialD; U+02146
-digamma; U+003DD
-disin; U+022F2
-div; U+000F7
-divide; U+000F7
-divide U+000F7
-divideontimes; U+022C7
-divonx; U+022C7
-DJcy; U+00402
-djcy; U+00452
-dlcorn; U+0231E
-dlcrop; U+0230D
-dollar; U+00024
-Dopf; U+1D53B
-dopf; U+1D555
-Dot; U+000A8
-dot; U+002D9
-DotDot; U+020DC
-doteq; U+02250
-doteqdot; U+02251
-DotEqual; U+02250
-dotminus; U+02238
-dotplus; U+02214
-dotsquare; U+022A1
-doublebarwedge; U+02306
-DoubleContourIntegral; U+0222F
-DoubleDot; U+000A8
-DoubleDownArrow; U+021D3
-DoubleLeftArrow; U+021D0
-DoubleLeftRightArrow; U+021D4
-DoubleLeftTee; U+02AE4
-DoubleLongLeftArrow; U+027F8
-DoubleLongLeftRightArrow; U+027FA
-DoubleLongRightArrow; U+027F9
-DoubleRightArrow; U+021D2
-DoubleRightTee; U+022A8
-DoubleUpArrow; U+021D1
-DoubleUpDownArrow; U+021D5
-DoubleVerticalBar; U+02225
-DownArrow; U+02193
-Downarrow; U+021D3
-downarrow; U+02193
-DownArrowBar; U+02913
-DownArrowUpArrow; U+021F5
-DownBreve; U+00311
-downdownarrows; U+021CA
-downharpoonleft; U+021C3
-downharpoonright; U+021C2
-DownLeftRightVector; U+02950
-DownLeftTeeVector; U+0295E
-DownLeftVector; U+021BD
-DownLeftVectorBar; U+02956
-DownRightTeeVector; U+0295F
-DownRightVector; U+021C1
-DownRightVectorBar; U+02957
-DownTee; U+022A4
-DownTeeArrow; U+021A7
-drbkarow; U+02910
-drcorn; U+0231F
-drcrop; U+0230C
-Dscr; U+1D49F
-dscr; U+1D4B9
-DScy; U+00405
-dscy; U+00455
-dsol; U+029F6
-Dstrok; U+00110
-dstrok; U+00111
-dtdot; U+022F1
-dtri; U+025BF
-dtrif; U+025BE
-duarr; U+021F5
-duhar; U+0296F
-dwangle; U+029A6
-DZcy; U+0040F
-dzcy; U+0045F
-dzigrarr; U+027FF
-Eacute; U+000C9
-Eacute U+000C9
-eacute; U+000E9
-eacute U+000E9
-easter; U+02A6E
-Ecaron; U+0011A
-ecaron; U+0011B
-ecir; U+02256
-Ecirc; U+000CA
-Ecirc U+000CA
-ecirc; U+000EA
-ecirc U+000EA
-ecolon; U+02255
-Ecy; U+0042D
-ecy; U+0044D
-eDDot; U+02A77
-Edot; U+00116
-eDot; U+02251
-edot; U+00117
-ee; U+02147
-efDot; U+02252
-Efr; U+1D508
-efr; U+1D522
-eg; U+02A9A
-Egrave; U+000C8
-Egrave U+000C8
-egrave; U+000E8
-egrave U+000E8
-egs; U+02A96
-egsdot; U+02A98
-el; U+02A99
-Element; U+02208
-elinters; U+023E7
-ell; U+02113
-els; U+02A95
-elsdot; U+02A97
-Emacr; U+00112
-emacr; U+00113
-empty; U+02205
-emptyset; U+02205
-EmptySmallSquare; U+025FB
-emptyv; U+02205
-EmptyVerySmallSquare; U+025AB
-emsp; U+02003
-emsp13; U+02004
-emsp14; U+02005
-ENG; U+0014A
-eng; U+0014B
-ensp; U+02002
-Eogon; U+00118
-eogon; U+00119
-Eopf; U+1D53C
-eopf; U+1D556
-epar; U+022D5
-eparsl; U+029E3
-eplus; U+02A71
-epsi; U+003B5
-Epsilon; U+00395
-epsilon; U+003B5
-epsiv; U+003F5
-eqcirc; U+02256
-eqcolon; U+02255
-eqsim; U+02242
-eqslantgtr; U+02A96
-eqslantless; U+02A95
-Equal; U+02A75
-equals; U+0003D
-EqualTilde; U+02242
-equest; U+0225F
-Equilibrium; U+021CC
-equiv; U+02261
-equivDD; U+02A78
-eqvparsl; U+029E5
-erarr; U+02971
-erDot; U+02253
-Escr; U+02130
-escr; U+0212F
-esdot; U+02250
-Esim; U+02A73
-esim; U+02242
-Eta; U+00397
-eta; U+003B7
-ETH; U+000D0
-ETH U+000D0
-eth; U+000F0
-eth U+000F0
-Euml; U+000CB
-Euml U+000CB
-euml; U+000EB
-euml U+000EB
-euro; U+020AC
-excl; U+00021
-exist; U+02203
-Exists; U+02203
-expectation; U+02130
-ExponentialE; U+02147
-exponentiale; U+02147
-fallingdotseq; U+02252
-Fcy; U+00424
-fcy; U+00444
-female; U+02640
-ffilig; U+0FB03
-fflig; U+0FB00
-ffllig; U+0FB04
-Ffr; U+1D509
-ffr; U+1D523
-filig; U+0FB01
-FilledSmallSquare; U+025FC
-FilledVerySmallSquare; U+025AA
-fjlig; U+00066 U+0006A
-flat; U+0266D
-fllig; U+0FB02
-fltns; U+025B1
-fnof; U+00192
-Fopf; U+1D53D
-fopf; U+1D557
-ForAll; U+02200
-forall; U+02200
-fork; U+022D4
-forkv; U+02AD9
-Fouriertrf; U+02131
-fpartint; U+02A0D
-frac12; U+000BD
-frac12 U+000BD
-frac13; U+02153
-frac14; U+000BC
-frac14 U+000BC
-frac15; U+02155
-frac16; U+02159
-frac18; U+0215B
-frac23; U+02154
-frac25; U+02156
-frac34; U+000BE
-frac34 U+000BE
-frac35; U+02157
-frac38; U+0215C
-frac45; U+02158
-frac56; U+0215A
-frac58; U+0215D
-frac78; U+0215E
-frasl; U+02044
-frown; U+02322
-Fscr; U+02131
-fscr; U+1D4BB
-gacute; U+001F5
-Gamma; U+00393
-gamma; U+003B3
-Gammad; U+003DC
-gammad; U+003DD
-gap; U+02A86
-Gbreve; U+0011E
-gbreve; U+0011F
-Gcedil; U+00122
-Gcirc; U+0011C
-gcirc; U+0011D
-Gcy; U+00413
-gcy; U+00433
-Gdot; U+00120
-gdot; U+00121
-gE; U+02267
-ge; U+02265
-gEl; U+02A8C
-gel; U+022DB
-geq; U+02265
-geqq; U+02267
-geqslant; U+02A7E
-ges; U+02A7E
-gescc; U+02AA9
-gesdot; U+02A80
-gesdoto; U+02A82
-gesdotol; U+02A84
-gesl; U+022DB U+0FE00
-gesles; U+02A94
-Gfr; U+1D50A
-gfr; U+1D524
-Gg; U+022D9
-gg; U+0226B
-ggg; U+022D9
-gimel; U+02137
-GJcy; U+00403
-gjcy; U+00453
-gl; U+02277
-gla; U+02AA5
-glE; U+02A92
-glj; U+02AA4
-gnap; U+02A8A
-gnapprox; U+02A8A
-gnE; U+02269
-gne; U+02A88
-gneq; U+02A88
-gneqq; U+02269
-gnsim; U+022E7
-Gopf; U+1D53E
-gopf; U+1D558
-grave; U+00060
-GreaterEqual; U+02265
-GreaterEqualLess; U+022DB
-GreaterFullEqual; U+02267
-GreaterGreater; U+02AA2
-GreaterLess; U+02277
-GreaterSlantEqual; U+02A7E
-GreaterTilde; U+02273
-Gscr; U+1D4A2
-gscr; U+0210A
-gsim; U+02273
-gsime; U+02A8E
-gsiml; U+02A90
-GT; U+0003E
-GT U+0003E
-Gt; U+0226B
-gt; U+0003E
-gt U+0003E
-gtcc; U+02AA7
-gtcir; U+02A7A
-gtdot; U+022D7
-gtlPar; U+02995
-gtquest; U+02A7C
-gtrapprox; U+02A86
-gtrarr; U+02978
-gtrdot; U+022D7
-gtreqless; U+022DB
-gtreqqless; U+02A8C
-gtrless; U+02277
-gtrsim; U+02273
-gvertneqq; U+02269 U+0FE00
-gvnE; U+02269 U+0FE00
-Hacek; U+002C7
-hairsp; U+0200A
-half; U+000BD
-hamilt; U+0210B
-HARDcy; U+0042A
-hardcy; U+0044A
-hArr; U+021D4
-harr; U+02194
-harrcir; U+02948
-harrw; U+021AD
-Hat; U+0005E
-hbar; U+0210F
-Hcirc; U+00124
-hcirc; U+00125
-hearts; U+02665
-heartsuit; U+02665
-hellip; U+02026
-hercon; U+022B9
-Hfr; U+0210C
-hfr; U+1D525
-HilbertSpace; U+0210B
-hksearow; U+02925
-hkswarow; U+02926
-hoarr; U+021FF
-homtht; U+0223B
-hookleftarrow; U+021A9
-hookrightarrow; U+021AA
-Hopf; U+0210D
-hopf; U+1D559
-horbar; U+02015
-HorizontalLine; U+02500
-Hscr; U+0210B
-hscr; U+1D4BD
-hslash; U+0210F
-Hstrok; U+00126
-hstrok; U+00127
-HumpDownHump; U+0224E
-HumpEqual; U+0224F
-hybull; U+02043
-hyphen; U+02010
-Iacute; U+000CD
-Iacute U+000CD
-iacute; U+000ED
-iacute U+000ED
-ic; U+02063
-Icirc; U+000CE
-Icirc U+000CE
-icirc; U+000EE
-icirc U+000EE
-Icy; U+00418
-icy; U+00438
-Idot; U+00130
-IEcy; U+00415
-iecy; U+00435
-iexcl; U+000A1
-iexcl U+000A1
-iff; U+021D4
-Ifr; U+02111
-ifr; U+1D526
-Igrave; U+000CC
-Igrave U+000CC
-igrave; U+000EC
-igrave U+000EC
-ii; U+02148
-iiiint; U+02A0C
-iiint; U+0222D
-iinfin; U+029DC
-iiota; U+02129
-IJlig; U+00132
-ijlig; U+00133
-Im; U+02111
-Imacr; U+0012A
-imacr; U+0012B
-image; U+02111
-ImaginaryI; U+02148
-imagline; U+02110
-imagpart; U+02111
-imath; U+00131
-imof; U+022B7
-imped; U+001B5
-Implies; U+021D2
-in; U+02208
-incare; U+02105
-infin; U+0221E
-infintie; U+029DD
-inodot; U+00131
-Int; U+0222C
-int; U+0222B
-intcal; U+022BA
-integers; U+02124
-Integral; U+0222B
-intercal; U+022BA
-Intersection; U+022C2
-intlarhk; U+02A17
-intprod; U+02A3C
-InvisibleComma; U+02063
-InvisibleTimes; U+02062
-IOcy; U+00401
-iocy; U+00451
-Iogon; U+0012E
-iogon; U+0012F
-Iopf; U+1D540
-iopf; U+1D55A
-Iota; U+00399
-iota; U+003B9
-iprod; U+02A3C
-iquest; U+000BF
-iquest U+000BF
-Iscr; U+02110
-iscr; U+1D4BE
-isin; U+02208
-isindot; U+022F5
-isinE; U+022F9
-isins; U+022F4
-isinsv; U+022F3
-isinv; U+02208
-it; U+02062
-Itilde; U+00128
-itilde; U+00129
-Iukcy; U+00406
-iukcy; U+00456
-Iuml; U+000CF
-Iuml U+000CF
-iuml; U+000EF
-iuml U+000EF
-Jcirc; U+00134
-jcirc; U+00135
-Jcy; U+00419
-jcy; U+00439
-Jfr; U+1D50D
-jfr; U+1D527
-jmath; U+00237
-Jopf; U+1D541
-jopf; U+1D55B
-Jscr; U+1D4A5
-jscr; U+1D4BF
-Jsercy; U+00408
-jsercy; U+00458
-Jukcy; U+00404
-jukcy; U+00454
-Kappa; U+0039A
-kappa; U+003BA
-kappav; U+003F0
-Kcedil; U+00136
-kcedil; U+00137
-Kcy; U+0041A
-kcy; U+0043A
-Kfr; U+1D50E
-kfr; U+1D528
-kgreen; U+00138
-KHcy; U+00425
-khcy; U+00445
-KJcy; U+0040C
-kjcy; U+0045C
-Kopf; U+1D542
-kopf; U+1D55C
-Kscr; U+1D4A6
-kscr; U+1D4C0
-lAarr; U+021DA
-Lacute; U+00139
-lacute; U+0013A
-laemptyv; U+029B4
-lagran; U+02112
-Lambda; U+0039B
-lambda; U+003BB
-Lang; U+027EA
-lang; U+027E8
-langd; U+02991
-langle; U+027E8
-lap; U+02A85
-Laplacetrf; U+02112
-laquo; U+000AB
-laquo U+000AB
-Larr; U+0219E
-lArr; U+021D0
-larr; U+02190
-larrb; U+021E4
-larrbfs; U+0291F
-larrfs; U+0291D
-larrhk; U+021A9
-larrlp; U+021AB
-larrpl; U+02939
-larrsim; U+02973
-larrtl; U+021A2
-lat; U+02AAB
-lAtail; U+0291B
-latail; U+02919
-late; U+02AAD
-lates; U+02AAD U+0FE00
-lBarr; U+0290E
-lbarr; U+0290C
-lbbrk; U+02772
-lbrace; U+0007B
-lbrack; U+0005B
-lbrke; U+0298B
-lbrksld; U+0298F
-lbrkslu; U+0298D
-Lcaron; U+0013D
-lcaron; U+0013E
-Lcedil; U+0013B
-lcedil; U+0013C
-lceil; U+02308
-lcub; U+0007B
-Lcy; U+0041B
-lcy; U+0043B
-ldca; U+02936
-ldquo; U+0201C
-ldquor; U+0201E
-ldrdhar; U+02967
-ldrushar; U+0294B
-ldsh; U+021B2
-lE; U+02266
-le; U+02264
-LeftAngleBracket; U+027E8
-LeftArrow; U+02190
-Leftarrow; U+021D0
-leftarrow; U+02190
-LeftArrowBar; U+021E4
-LeftArrowRightArrow; U+021C6
-leftarrowtail; U+021A2
-LeftCeiling; U+02308
-LeftDoubleBracket; U+027E6
-LeftDownTeeVector; U+02961
-LeftDownVector; U+021C3
-LeftDownVectorBar; U+02959
-LeftFloor; U+0230A
-leftharpoondown; U+021BD
-leftharpoonup; U+021BC
-leftleftarrows; U+021C7
-LeftRightArrow; U+02194
-Leftrightarrow; U+021D4
-leftrightarrow; U+02194
-leftrightarrows; U+021C6
-leftrightharpoons; U+021CB
-leftrightsquigarrow; U+021AD
-LeftRightVector; U+0294E
-LeftTee; U+022A3
-LeftTeeArrow; U+021A4
-LeftTeeVector; U+0295A
-leftthreetimes; U+022CB
-LeftTriangle; U+022B2
-LeftTriangleBar; U+029CF
-LeftTriangleEqual; U+022B4
-LeftUpDownVector; U+02951
-LeftUpTeeVector; U+02960
-LeftUpVector; U+021BF
-LeftUpVectorBar; U+02958
-LeftVector; U+021BC
-LeftVectorBar; U+02952
-lEg; U+02A8B
-leg; U+022DA
-leq; U+02264
-leqq; U+02266
-leqslant; U+02A7D
-les; U+02A7D
-lescc; U+02AA8
-lesdot; U+02A7F
-lesdoto; U+02A81
-lesdotor; U+02A83
-lesg; U+022DA U+0FE00
-lesges; U+02A93
-lessapprox; U+02A85
-lessdot; U+022D6
-lesseqgtr; U+022DA
-lesseqqgtr; U+02A8B
-LessEqualGreater; U+022DA
-LessFullEqual; U+02266
-LessGreater; U+02276
-lessgtr; U+02276
-LessLess; U+02AA1
-lesssim; U+02272
-LessSlantEqual; U+02A7D
-LessTilde; U+02272
-lfisht; U+0297C
-lfloor; U+0230A
-Lfr; U+1D50F
-lfr; U+1D529
-lg; U+02276
-lgE; U+02A91
-lHar; U+02962
-lhard; U+021BD
-lharu; U+021BC
-lharul; U+0296A
-lhblk; U+02584
-LJcy; U+00409
-ljcy; U+00459
-Ll; U+022D8
-ll; U+0226A
-llarr; U+021C7
-llcorner; U+0231E
-Lleftarrow; U+021DA
-llhard; U+0296B
-lltri; U+025FA
-Lmidot; U+0013F
-lmidot; U+00140
-lmoust; U+023B0
-lmoustache; U+023B0
-lnap; U+02A89
-lnapprox; U+02A89
-lnE; U+02268
-lne; U+02A87
-lneq; U+02A87
-lneqq; U+02268
-lnsim; U+022E6
-loang; U+027EC
-loarr; U+021FD
-lobrk; U+027E6
-LongLeftArrow; U+027F5
-Longleftarrow; U+027F8
-longleftarrow; U+027F5
-LongLeftRightArrow; U+027F7
-Longleftrightarrow; U+027FA
-longleftrightarrow; U+027F7
-longmapsto; U+027FC
-LongRightArrow; U+027F6
-Longrightarrow; U+027F9
-longrightarrow; U+027F6
-looparrowleft; U+021AB
-looparrowright; U+021AC
-lopar; U+02985
-Lopf; U+1D543
-lopf; U+1D55D
-loplus; U+02A2D
-lotimes; U+02A34
-lowast; U+02217
-lowbar; U+0005F
-LowerLeftArrow; U+02199
-LowerRightArrow; U+02198
-loz; U+025CA
-lozenge; U+025CA
-lozf; U+029EB
-lpar; U+00028
-lparlt; U+02993
-lrarr; U+021C6
-lrcorner; U+0231F
-lrhar; U+021CB
-lrhard; U+0296D
-lrm; U+0200E
-lrtri; U+022BF
-lsaquo; U+02039
-Lscr; U+02112
-lscr; U+1D4C1
-Lsh; U+021B0
-lsh; U+021B0
-lsim; U+02272
-lsime; U+02A8D
-lsimg; U+02A8F
-lsqb; U+0005B
-lsquo; U+02018
-lsquor; U+0201A
-Lstrok; U+00141
-lstrok; U+00142
-LT; U+0003C
-LT U+0003C
-Lt; U+0226A
-lt; U+0003C
-lt U+0003C
-ltcc; U+02AA6
-ltcir; U+02A79
-ltdot; U+022D6
-lthree; U+022CB
-ltimes; U+022C9
-ltlarr; U+02976
-ltquest; U+02A7B
-ltri; U+025C3
-ltrie; U+022B4
-ltrif; U+025C2
-ltrPar; U+02996
-lurdshar; U+0294A
-luruhar; U+02966
-lvertneqq; U+02268 U+0FE00
-lvnE; U+02268 U+0FE00
-macr; U+000AF
-macr U+000AF
-male; U+02642
-malt; U+02720
-maltese; U+02720
-Map; U+02905
-map; U+021A6
-mapsto; U+021A6
-mapstodown; U+021A7
-mapstoleft; U+021A4
-mapstoup; U+021A5
-marker; U+025AE
-mcomma; U+02A29
-Mcy; U+0041C
-mcy; U+0043C
-mdash; U+02014
-mDDot; U+0223A
-measuredangle; U+02221
-MediumSpace; U+0205F
-Mellintrf; U+02133
-Mfr; U+1D510
-mfr; U+1D52A
-mho; U+02127
-micro; U+000B5
-micro U+000B5
-mid; U+02223
-midast; U+0002A
-midcir; U+02AF0
-middot; U+000B7
-middot U+000B7
-minus; U+02212
-minusb; U+0229F
-minusd; U+02238
-minusdu; U+02A2A
-MinusPlus; U+02213
-mlcp; U+02ADB
-mldr; U+02026
-mnplus; U+02213
-models; U+022A7
-Mopf; U+1D544
-mopf; U+1D55E
-mp; U+02213
-Mscr; U+02133
-mscr; U+1D4C2
-mstpos; U+0223E
-Mu; U+0039C
-mu; U+003BC
-multimap; U+022B8
-mumap; U+022B8
-nabla; U+02207
-Nacute; U+00143
-nacute; U+00144
-nang; U+02220 U+020D2
-nap; U+02249
-napE; U+02A70 U+00338
-napid; U+0224B U+00338
-napos; U+00149
-napprox; U+02249
-natur; U+0266E
-natural; U+0266E
-naturals; U+02115
-nbsp; U+000A0
-nbsp U+000A0
-nbump; U+0224E U+00338
-nbumpe; U+0224F U+00338
-ncap; U+02A43
-Ncaron; U+00147
-ncaron; U+00148
-Ncedil; U+00145
-ncedil; U+00146
-ncong; U+02247
-ncongdot; U+02A6D U+00338
-ncup; U+02A42
-Ncy; U+0041D
-ncy; U+0043D
-ndash; U+02013
-ne; U+02260
-nearhk; U+02924
-neArr; U+021D7
-nearr; U+02197
-nearrow; U+02197
-nedot; U+02250 U+00338
-NegativeMediumSpace; U+0200B
-NegativeThickSpace; U+0200B
-NegativeThinSpace; U+0200B
-NegativeVeryThinSpace; U+0200B
-nequiv; U+02262
-nesear; U+02928
-nesim; U+02242 U+00338
-NestedGreaterGreater; U+0226B
-NestedLessLess; U+0226A
-NewLine; U+0000A
-nexist; U+02204
-nexists; U+02204
-Nfr; U+1D511
-nfr; U+1D52B
-ngE; U+02267 U+00338
-nge; U+02271
-ngeq; U+02271
-ngeqq; U+02267 U+00338
-ngeqslant; U+02A7E U+00338
-nges; U+02A7E U+00338
-nGg; U+022D9 U+00338
-ngsim; U+02275
-nGt; U+0226B U+020D2
-ngt; U+0226F
-ngtr; U+0226F
-nGtv; U+0226B U+00338
-nhArr; U+021CE
-nharr; U+021AE
-nhpar; U+02AF2
-ni; U+0220B
-nis; U+022FC
-nisd; U+022FA
-niv; U+0220B
-NJcy; U+0040A
-njcy; U+0045A
-nlArr; U+021CD
-nlarr; U+0219A
-nldr; U+02025
-nlE; U+02266 U+00338
-nle; U+02270
-nLeftarrow; U+021CD
-nleftarrow; U+0219A
-nLeftrightarrow; U+021CE
-nleftrightarrow; U+021AE
-nleq; U+02270
-nleqq; U+02266 U+00338
-nleqslant; U+02A7D U+00338
-nles; U+02A7D U+00338
-nless; U+0226E
-nLl; U+022D8 U+00338
-nlsim; U+02274
-nLt; U+0226A U+020D2
-nlt; U+0226E
-nltri; U+022EA
-nltrie; U+022EC
-nLtv; U+0226A U+00338
-nmid; U+02224
-NoBreak; U+02060
-NonBreakingSpace; U+000A0
-Nopf; U+02115
-nopf; U+1D55F
-Not; U+02AEC
-not; U+000AC
-not U+000AC
-NotCongruent; U+02262
-NotCupCap; U+0226D
-NotDoubleVerticalBar; U+02226
-NotElement; U+02209
-NotEqual; U+02260
-NotEqualTilde; U+02242 U+00338
-NotExists; U+02204
-NotGreater; U+0226F
-NotGreaterEqual; U+02271
-NotGreaterFullEqual; U+02267 U+00338
-NotGreaterGreater; U+0226B U+00338
-NotGreaterLess; U+02279
-NotGreaterSlantEqual; U+02A7E U+00338
-NotGreaterTilde; U+02275
-NotHumpDownHump; U+0224E U+00338
-NotHumpEqual; U+0224F U+00338
-notin; U+02209
-notindot; U+022F5 U+00338
-notinE; U+022F9 U+00338
-notinva; U+02209
-notinvb; U+022F7
-notinvc; U+022F6
-NotLeftTriangle; U+022EA
-NotLeftTriangleBar; U+029CF U+00338
-NotLeftTriangleEqual; U+022EC
-NotLess; U+0226E
-NotLessEqual; U+02270
-NotLessGreater; U+02278
-NotLessLess; U+0226A U+00338
-NotLessSlantEqual; U+02A7D U+00338
-NotLessTilde; U+02274
-NotNestedGreaterGreater; U+02AA2 U+00338
-NotNestedLessLess; U+02AA1 U+00338
-notni; U+0220C
-notniva; U+0220C
-notnivb; U+022FE
-notnivc; U+022FD
-NotPrecedes; U+02280
-NotPrecedesEqual; U+02AAF U+00338
-NotPrecedesSlantEqual; U+022E0
-NotReverseElement; U+0220C
-NotRightTriangle; U+022EB
-NotRightTriangleBar; U+029D0 U+00338
-NotRightTriangleEqual; U+022ED
-NotSquareSubset; U+0228F U+00338
-NotSquareSubsetEqual; U+022E2
-NotSquareSuperset; U+02290 U+00338
-NotSquareSupersetEqual; U+022E3
-NotSubset; U+02282 U+020D2
-NotSubsetEqual; U+02288
-NotSucceeds; U+02281
-NotSucceedsEqual; U+02AB0 U+00338
-NotSucceedsSlantEqual; U+022E1
-NotSucceedsTilde; U+0227F U+00338
-NotSuperset; U+02283 U+020D2
-NotSupersetEqual; U+02289
-NotTilde; U+02241
-NotTildeEqual; U+02244
-NotTildeFullEqual; U+02247
-NotTildeTilde; U+02249
-NotVerticalBar; U+02224
-npar; U+02226
-nparallel; U+02226
-nparsl; U+02AFD U+020E5
-npart; U+02202 U+00338
-npolint; U+02A14
-npr; U+02280
-nprcue; U+022E0
-npre; U+02AAF U+00338
-nprec; U+02280
-npreceq; U+02AAF U+00338
-nrArr; U+021CF
-nrarr; U+0219B
-nrarrc; U+02933 U+00338
-nrarrw; U+0219D U+00338
-nRightarrow; U+021CF
-nrightarrow; U+0219B
-nrtri; U+022EB
-nrtrie; U+022ED
-nsc; U+02281
-nsccue; U+022E1
-nsce; U+02AB0 U+00338
-Nscr; U+1D4A9
-nscr; U+1D4C3
-nshortmid; U+02224
-nshortparallel; U+02226
-nsim; U+02241
-nsime; U+02244
-nsimeq; U+02244
-nsmid; U+02224
-nspar; U+02226
-nsqsube; U+022E2
-nsqsupe; U+022E3
-nsub; U+02284
-nsubE; U+02AC5 U+00338
-nsube; U+02288
-nsubset; U+02282 U+020D2
-nsubseteq; U+02288
-nsubseteqq; U+02AC5 U+00338
-nsucc; U+02281
-nsucceq; U+02AB0 U+00338
-nsup; U+02285
-nsupE; U+02AC6 U+00338
-nsupe; U+02289
-nsupset; U+02283 U+020D2
-nsupseteq; U+02289
-nsupseteqq; U+02AC6 U+00338
-ntgl; U+02279
-Ntilde; U+000D1
-Ntilde U+000D1
-ntilde; U+000F1
-ntilde U+000F1
-ntlg; U+02278
-ntriangleleft; U+022EA
-ntrianglelefteq; U+022EC
-ntriangleright; U+022EB
-ntrianglerighteq; U+022ED
-Nu; U+0039D
-nu; U+003BD
-num; U+00023
-numero; U+02116
-numsp; U+02007
-nvap; U+0224D U+020D2
-nVDash; U+022AF
-nVdash; U+022AE
-nvDash; U+022AD
-nvdash; U+022AC
-nvge; U+02265 U+020D2
-nvgt; U+0003E U+020D2
-nvHarr; U+02904
-nvinfin; U+029DE
-nvlArr; U+02902
-nvle; U+02264 U+020D2
-nvlt; U+0003C U+020D2
-nvltrie; U+022B4 U+020D2
-nvrArr; U+02903
-nvrtrie; U+022B5 U+020D2
-nvsim; U+0223C U+020D2
-nwarhk; U+02923
-nwArr; U+021D6
-nwarr; U+02196
-nwarrow; U+02196
-nwnear; U+02927
-Oacute; U+000D3
-Oacute U+000D3
-oacute; U+000F3
-oacute U+000F3
-oast; U+0229B
-ocir; U+0229A
-Ocirc; U+000D4
-Ocirc U+000D4
-ocirc; U+000F4
-ocirc U+000F4
-Ocy; U+0041E
-ocy; U+0043E
-odash; U+0229D
-Odblac; U+00150
-odblac; U+00151
-odiv; U+02A38
-odot; U+02299
-odsold; U+029BC
-OElig; U+00152
-oelig; U+00153
-ofcir; U+029BF
-Ofr; U+1D512
-ofr; U+1D52C
-ogon; U+002DB
-Ograve; U+000D2
-Ograve U+000D2
-ograve; U+000F2
-ograve U+000F2
-ogt; U+029C1
-ohbar; U+029B5
-ohm; U+003A9
-oint; U+0222E
-olarr; U+021BA
-olcir; U+029BE
-olcross; U+029BB
-oline; U+0203E
-olt; U+029C0
-Omacr; U+0014C
-omacr; U+0014D
-Omega; U+003A9
-omega; U+003C9
-Omicron; U+0039F
-omicron; U+003BF
-omid; U+029B6
-ominus; U+02296
-Oopf; U+1D546
-oopf; U+1D560
-opar; U+029B7
-OpenCurlyDoubleQuote; U+0201C
-OpenCurlyQuote; U+02018
-operp; U+029B9
-oplus; U+02295
-Or; U+02A54
-or; U+02228
-orarr; U+021BB
-ord; U+02A5D
-order; U+02134
-orderof; U+02134
-ordf; U+000AA
-ordf U+000AA
-ordm; U+000BA
-ordm U+000BA
-origof; U+022B6
-oror; U+02A56
-orslope; U+02A57
-orv; U+02A5B
-oS; U+024C8
-Oscr; U+1D4AA
-oscr; U+02134
-Oslash; U+000D8
-Oslash U+000D8
-oslash; U+000F8
-oslash U+000F8
-osol; U+02298
-Otilde; U+000D5
-Otilde U+000D5
-otilde; U+000F5
-otilde U+000F5
-Otimes; U+02A37
-otimes; U+02297
-otimesas; U+02A36
-Ouml; U+000D6
-Ouml U+000D6
-ouml; U+000F6
-ouml U+000F6
-ovbar; U+0233D
-OverBar; U+0203E
-OverBrace; U+023DE
-OverBracket; U+023B4
-OverParenthesis; U+023DC
-par; U+02225
-para; U+000B6
-para U+000B6
-parallel; U+02225
-parsim; U+02AF3
-parsl; U+02AFD
-part; U+02202
-PartialD; U+02202
-Pcy; U+0041F
-pcy; U+0043F
-percnt; U+00025
-period; U+0002E
-permil; U+02030
-perp; U+022A5
-pertenk; U+02031
-Pfr; U+1D513
-pfr; U+1D52D
-Phi; U+003A6
-phi; U+003C6
-phiv; U+003D5
-phmmat; U+02133
-phone; U+0260E
-Pi; U+003A0
-pi; U+003C0
-pitchfork; U+022D4
-piv; U+003D6
-planck; U+0210F
-planckh; U+0210E
-plankv; U+0210F
-plus; U+0002B
-plusacir; U+02A23
-plusb; U+0229E
-pluscir; U+02A22
-plusdo; U+02214
-plusdu; U+02A25
-pluse; U+02A72
-PlusMinus; U+000B1
-plusmn; U+000B1
-plusmn U+000B1
-plussim; U+02A26
-plustwo; U+02A27
-pm; U+000B1
-Poincareplane; U+0210C
-pointint; U+02A15
-Popf; U+02119
-popf; U+1D561
-pound; U+000A3
-pound U+000A3
-Pr; U+02ABB
-pr; U+0227A
-prap; U+02AB7
-prcue; U+0227C
-prE; U+02AB3
-pre; U+02AAF
-prec; U+0227A
-precapprox; U+02AB7
-preccurlyeq; U+0227C
-Precedes; U+0227A
-PrecedesEqual; U+02AAF
-PrecedesSlantEqual; U+0227C
-PrecedesTilde; U+0227E
-preceq; U+02AAF
-precnapprox; U+02AB9
-precneqq; U+02AB5
-precnsim; U+022E8
-precsim; U+0227E
-Prime; U+02033
-prime; U+02032
-primes; U+02119
-prnap; U+02AB9
-prnE; U+02AB5
-prnsim; U+022E8
-prod; U+0220F
-Product; U+0220F
-profalar; U+0232E
-profline; U+02312
-profsurf; U+02313
-prop; U+0221D
-Proportion; U+02237
-Proportional; U+0221D
-propto; U+0221D
-prsim; U+0227E
-prurel; U+022B0
-Pscr; U+1D4AB
-pscr; U+1D4C5
-Psi; U+003A8
-psi; U+003C8
-puncsp; U+02008
-Qfr; U+1D514
-qfr; U+1D52E
-qint; U+02A0C
-Qopf; U+0211A
-qopf; U+1D562
-qprime; U+02057
-Qscr; U+1D4AC
-qscr; U+1D4C6
-quaternions; U+0210D
-quatint; U+02A16
-quest; U+0003F
-questeq; U+0225F
-QUOT; U+00022
-QUOT U+00022
-quot; U+00022
-quot U+00022
-rAarr; U+021DB
-race; U+0223D U+00331
-Racute; U+00154
-racute; U+00155
-radic; U+0221A
-raemptyv; U+029B3
-Rang; U+027EB
-rang; U+027E9
-rangd; U+02992
-range; U+029A5
-rangle; U+027E9
-raquo; U+000BB
-raquo U+000BB
-Rarr; U+021A0
-rArr; U+021D2
-rarr; U+02192
-rarrap; U+02975
-rarrb; U+021E5
-rarrbfs; U+02920
-rarrc; U+02933
-rarrfs; U+0291E
-rarrhk; U+021AA
-rarrlp; U+021AC
-rarrpl; U+02945
-rarrsim; U+02974
-Rarrtl; U+02916
-rarrtl; U+021A3
-rarrw; U+0219D
-rAtail; U+0291C
-ratail; U+0291A
-ratio; U+02236
-rationals; U+0211A
-RBarr; U+02910
-rBarr; U+0290F
-rbarr; U+0290D
-rbbrk; U+02773
-rbrace; U+0007D
-rbrack; U+0005D
-rbrke; U+0298C
-rbrksld; U+0298E
-rbrkslu; U+02990
-Rcaron; U+00158
-rcaron; U+00159
-Rcedil; U+00156
-rcedil; U+00157
-rceil; U+02309
-rcub; U+0007D
-Rcy; U+00420
-rcy; U+00440
-rdca; U+02937
-rdldhar; U+02969
-rdquo; U+0201D
-rdquor; U+0201D
-rdsh; U+021B3
-Re; U+0211C
-real; U+0211C
-realine; U+0211B
-realpart; U+0211C
-reals; U+0211D
-rect; U+025AD
-REG; U+000AE
-REG U+000AE
-reg; U+000AE
-reg U+000AE
-ReverseElement; U+0220B
-ReverseEquilibrium; U+021CB
-ReverseUpEquilibrium; U+0296F
-rfisht; U+0297D
-rfloor; U+0230B
-Rfr; U+0211C
-rfr; U+1D52F
-rHar; U+02964
-rhard; U+021C1
-rharu; U+021C0
-rharul; U+0296C
-Rho; U+003A1
-rho; U+003C1
-rhov; U+003F1
-RightAngleBracket; U+027E9
-RightArrow; U+02192
-Rightarrow; U+021D2
-rightarrow; U+02192
-RightArrowBar; U+021E5
-RightArrowLeftArrow; U+021C4
-rightarrowtail; U+021A3
-RightCeiling; U+02309
-RightDoubleBracket; U+027E7
-RightDownTeeVector; U+0295D
-RightDownVector; U+021C2
-RightDownVectorBar; U+02955
-RightFloor; U+0230B
-rightharpoondown; U+021C1
-rightharpoonup; U+021C0
-rightleftarrows; U+021C4
-rightleftharpoons; U+021CC
-rightrightarrows; U+021C9
-rightsquigarrow; U+0219D
-RightTee; U+022A2
-RightTeeArrow; U+021A6
-RightTeeVector; U+0295B
-rightthreetimes; U+022CC
-RightTriangle; U+022B3
-RightTriangleBar; U+029D0
-RightTriangleEqual; U+022B5
-RightUpDownVector; U+0294F
-RightUpTeeVector; U+0295C
-RightUpVector; U+021BE
-RightUpVectorBar; U+02954
-RightVector; U+021C0
-RightVectorBar; U+02953
-ring; U+002DA
-risingdotseq; U+02253
-rlarr; U+021C4
-rlhar; U+021CC
-rlm; U+0200F
-rmoust; U+023B1
-rmoustache; U+023B1
-rnmid; U+02AEE
-roang; U+027ED
-roarr; U+021FE
-robrk; U+027E7
-ropar; U+02986
-Ropf; U+0211D
-ropf; U+1D563
-roplus; U+02A2E
-rotimes; U+02A35
-RoundImplies; U+02970
-rpar; U+00029
-rpargt; U+02994
-rppolint; U+02A12
-rrarr; U+021C9
-Rrightarrow; U+021DB
-rsaquo; U+0203A
-Rscr; U+0211B
-rscr; U+1D4C7
-Rsh; U+021B1
-rsh; U+021B1
-rsqb; U+0005D
-rsquo; U+02019
-rsquor; U+02019
-rthree; U+022CC
-rtimes; U+022CA
-rtri; U+025B9
-rtrie; U+022B5
-rtrif; U+025B8
-rtriltri; U+029CE
-RuleDelayed; U+029F4
-ruluhar; U+02968
-rx; U+0211E
-Sacute; U+0015A
-sacute; U+0015B
-sbquo; U+0201A
-Sc; U+02ABC
-sc; U+0227B
-scap; U+02AB8
-Scaron; U+00160
-scaron; U+00161
-sccue; U+0227D
-scE; U+02AB4
-sce; U+02AB0
-Scedil; U+0015E
-scedil; U+0015F
-Scirc; U+0015C
-scirc; U+0015D
-scnap; U+02ABA
-scnE; U+02AB6
-scnsim; U+022E9
-scpolint; U+02A13
-scsim; U+0227F
-Scy; U+00421
-scy; U+00441
-sdot; U+022C5
-sdotb; U+022A1
-sdote; U+02A66
-searhk; U+02925
-seArr; U+021D8
-searr; U+02198
-searrow; U+02198
-sect; U+000A7
-sect U+000A7
-semi; U+0003B
-seswar; U+02929
-setminus; U+02216
-setmn; U+02216
-sext; U+02736
-Sfr; U+1D516
-sfr; U+1D530
-sfrown; U+02322
-sharp; U+0266F
-SHCHcy; U+00429
-shchcy; U+00449
-SHcy; U+00428
-shcy; U+00448
-ShortDownArrow; U+02193
-ShortLeftArrow; U+02190
-shortmid; U+02223
-shortparallel; U+02225
-ShortRightArrow; U+02192
-ShortUpArrow; U+02191
-shy; U+000AD
-shy U+000AD
-Sigma; U+003A3
-sigma; U+003C3
-sigmaf; U+003C2
-sigmav; U+003C2
-sim; U+0223C
-simdot; U+02A6A
-sime; U+02243
-simeq; U+02243
-simg; U+02A9E
-simgE; U+02AA0
-siml; U+02A9D
-simlE; U+02A9F
-simne; U+02246
-simplus; U+02A24
-simrarr; U+02972
-slarr; U+02190
-SmallCircle; U+02218
-smallsetminus; U+02216
-smashp; U+02A33
-smeparsl; U+029E4
-smid; U+02223
-smile; U+02323
-smt; U+02AAA
-smte; U+02AAC
-smtes; U+02AAC U+0FE00
-SOFTcy; U+0042C
-softcy; U+0044C
-sol; U+0002F
-solb; U+029C4
-solbar; U+0233F
-Sopf; U+1D54A
-sopf; U+1D564
-spades; U+02660
-spadesuit; U+02660
-spar; U+02225
-sqcap; U+02293
-sqcaps; U+02293 U+0FE00
-sqcup; U+02294
-sqcups; U+02294 U+0FE00
-Sqrt; U+0221A
-sqsub; U+0228F
-sqsube; U+02291
-sqsubset; U+0228F
-sqsubseteq; U+02291
-sqsup; U+02290
-sqsupe; U+02292
-sqsupset; U+02290
-sqsupseteq; U+02292
-squ; U+025A1
-Square; U+025A1
-square; U+025A1
-SquareIntersection; U+02293
-SquareSubset; U+0228F
-SquareSubsetEqual; U+02291
-SquareSuperset; U+02290
-SquareSupersetEqual; U+02292
-SquareUnion; U+02294
-squarf; U+025AA
-squf; U+025AA
-srarr; U+02192
-Sscr; U+1D4AE
-sscr; U+1D4C8
-ssetmn; U+02216
-ssmile; U+02323
-sstarf; U+022C6
-Star; U+022C6
-star; U+02606
-starf; U+02605
-straightepsilon; U+003F5
-straightphi; U+003D5
-strns; U+000AF
-Sub; U+022D0
-sub; U+02282
-subdot; U+02ABD
-subE; U+02AC5
-sube; U+02286
-subedot; U+02AC3
-submult; U+02AC1
-subnE; U+02ACB
-subne; U+0228A
-subplus; U+02ABF
-subrarr; U+02979
-Subset; U+022D0
-subset; U+02282
-subseteq; U+02286
-subseteqq; U+02AC5
-SubsetEqual; U+02286
-subsetneq; U+0228A
-subsetneqq; U+02ACB
-subsim; U+02AC7
-subsub; U+02AD5
-subsup; U+02AD3
-succ; U+0227B
-succapprox; U+02AB8
-succcurlyeq; U+0227D
-Succeeds; U+0227B
-SucceedsEqual; U+02AB0
-SucceedsSlantEqual; U+0227D
-SucceedsTilde; U+0227F
-succeq; U+02AB0
-succnapprox; U+02ABA
-succneqq; U+02AB6
-succnsim; U+022E9
-succsim; U+0227F
-SuchThat; U+0220B
-Sum; U+02211
-sum; U+02211
-sung; U+0266A
-Sup; U+022D1
-sup; U+02283
-sup1; U+000B9
-sup1 U+000B9
-sup2; U+000B2
-sup2 U+000B2
-sup3; U+000B3
-sup3 U+000B3
-supdot; U+02ABE
-supdsub; U+02AD8
-supE; U+02AC6
-supe; U+02287
-supedot; U+02AC4
-Superset; U+02283
-SupersetEqual; U+02287
-suphsol; U+027C9
-suphsub; U+02AD7
-suplarr; U+0297B
-supmult; U+02AC2
-supnE; U+02ACC
-supne; U+0228B
-supplus; U+02AC0
-Supset; U+022D1
-supset; U+02283
-supseteq; U+02287
-supseteqq; U+02AC6
-supsetneq; U+0228B
-supsetneqq; U+02ACC
-supsim; U+02AC8
-supsub; U+02AD4
-supsup; U+02AD6
-swarhk; U+02926
-swArr; U+021D9
-swarr; U+02199
-swarrow; U+02199
-swnwar; U+0292A
-szlig; U+000DF
-szlig U+000DF
-Tab; U+00009
-target; U+02316
-Tau; U+003A4
-tau; U+003C4
-tbrk; U+023B4
-Tcaron; U+00164
-tcaron; U+00165
-Tcedil; U+00162
-tcedil; U+00163
-Tcy; U+00422
-tcy; U+00442
-tdot; U+020DB
-telrec; U+02315
-Tfr; U+1D517
-tfr; U+1D531
-there4; U+02234
-Therefore; U+02234
-therefore; U+02234
-Theta; U+00398
-theta; U+003B8
-thetasym; U+003D1
-thetav; U+003D1
-thickapprox; U+02248
-thicksim; U+0223C
-ThickSpace; U+0205F U+0200A
-thinsp; U+02009
-ThinSpace; U+02009
-thkap; U+02248
-thksim; U+0223C
-THORN; U+000DE
-THORN U+000DE
-thorn; U+000FE
-thorn U+000FE
-Tilde; U+0223C
-tilde; U+002DC
-TildeEqual; U+02243
-TildeFullEqual; U+02245
-TildeTilde; U+02248
-times; U+000D7
-times U+000D7
-timesb; U+022A0
-timesbar; U+02A31
-timesd; U+02A30
-tint; U+0222D
-toea; U+02928
-top; U+022A4
-topbot; U+02336
-topcir; U+02AF1
-Topf; U+1D54B
-topf; U+1D565
-topfork; U+02ADA
-tosa; U+02929
-tprime; U+02034
-TRADE; U+02122
-trade; U+02122
-triangle; U+025B5
-triangledown; U+025BF
-triangleleft; U+025C3
-trianglelefteq; U+022B4
-triangleq; U+0225C
-triangleright; U+025B9
-trianglerighteq; U+022B5
-tridot; U+025EC
-trie; U+0225C
-triminus; U+02A3A
-TripleDot; U+020DB
-triplus; U+02A39
-trisb; U+029CD
-tritime; U+02A3B
-trpezium; U+023E2
-Tscr; U+1D4AF
-tscr; U+1D4C9
-TScy; U+00426
-tscy; U+00446
-TSHcy; U+0040B
-tshcy; U+0045B
-Tstrok; U+00166
-tstrok; U+00167
-twixt; U+0226C
-twoheadleftarrow; U+0219E
-twoheadrightarrow; U+021A0
-Uacute; U+000DA
-Uacute U+000DA
-uacute; U+000FA
-uacute U+000FA
-Uarr; U+0219F
-uArr; U+021D1
-uarr; U+02191
-Uarrocir; U+02949
-Ubrcy; U+0040E
-ubrcy; U+0045E
-Ubreve; U+0016C
-ubreve; U+0016D
-Ucirc; U+000DB
-Ucirc U+000DB
-ucirc; U+000FB
-ucirc U+000FB
-Ucy; U+00423
-ucy; U+00443
-udarr; U+021C5
-Udblac; U+00170
-udblac; U+00171
-udhar; U+0296E
-ufisht; U+0297E
-Ufr; U+1D518
-ufr; U+1D532
-Ugrave; U+000D9
-Ugrave U+000D9
-ugrave; U+000F9
-ugrave U+000F9
-uHar; U+02963
-uharl; U+021BF
-uharr; U+021BE
-uhblk; U+02580
-ulcorn; U+0231C
-ulcorner; U+0231C
-ulcrop; U+0230F
-ultri; U+025F8
-Umacr; U+0016A
-umacr; U+0016B
-uml; U+000A8
-uml U+000A8
-UnderBar; U+0005F
-UnderBrace; U+023DF
-UnderBracket; U+023B5
-UnderParenthesis; U+023DD
-Union; U+022C3
-UnionPlus; U+0228E
-Uogon; U+00172
-uogon; U+00173
-Uopf; U+1D54C
-uopf; U+1D566
-UpArrow; U+02191
-Uparrow; U+021D1
-uparrow; U+02191
-UpArrowBar; U+02912
-UpArrowDownArrow; U+021C5
-UpDownArrow; U+02195
-Updownarrow; U+021D5
-updownarrow; U+02195
-UpEquilibrium; U+0296E
-upharpoonleft; U+021BF
-upharpoonright; U+021BE
-uplus; U+0228E
-UpperLeftArrow; U+02196
-UpperRightArrow; U+02197
-Upsi; U+003D2
-upsi; U+003C5
-upsih; U+003D2
-Upsilon; U+003A5
-upsilon; U+003C5
-UpTee; U+022A5
-UpTeeArrow; U+021A5
-upuparrows; U+021C8
-urcorn; U+0231D
-urcorner; U+0231D
-urcrop; U+0230E
-Uring; U+0016E
-uring; U+0016F
-urtri; U+025F9
-Uscr; U+1D4B0
-uscr; U+1D4CA
-utdot; U+022F0
-Utilde; U+00168
-utilde; U+00169
-utri; U+025B5
-utrif; U+025B4
-uuarr; U+021C8
-Uuml; U+000DC
-Uuml U+000DC
-uuml; U+000FC
-uuml U+000FC
-uwangle; U+029A7
-vangrt; U+0299C
-varepsilon; U+003F5
-varkappa; U+003F0
-varnothing; U+02205
-varphi; U+003D5
-varpi; U+003D6
-varpropto; U+0221D
-vArr; U+021D5
-varr; U+02195
-varrho; U+003F1
-varsigma; U+003C2
-varsubsetneq; U+0228A U+0FE00
-varsubsetneqq; U+02ACB U+0FE00
-varsupsetneq; U+0228B U+0FE00
-varsupsetneqq; U+02ACC U+0FE00
-vartheta; U+003D1
-vartriangleleft; U+022B2
-vartriangleright; U+022B3
-Vbar; U+02AEB
-vBar; U+02AE8
-vBarv; U+02AE9
-Vcy; U+00412
-vcy; U+00432
-VDash; U+022AB
-Vdash; U+022A9
-vDash; U+022A8
-vdash; U+022A2
-Vdashl; U+02AE6
-Vee; U+022C1
-vee; U+02228
-veebar; U+022BB
-veeeq; U+0225A
-vellip; U+022EE
-Verbar; U+02016
-verbar; U+0007C
-Vert; U+02016
-vert; U+0007C
-VerticalBar; U+02223
-VerticalLine; U+0007C
-VerticalSeparator; U+02758
-VerticalTilde; U+02240
-VeryThinSpace; U+0200A
-Vfr; U+1D519
-vfr; U+1D533
-vltri; U+022B2
-vnsub; U+02282 U+020D2
-vnsup; U+02283 U+020D2
-Vopf; U+1D54D
-vopf; U+1D567
-vprop; U+0221D
-vrtri; U+022B3
-Vscr; U+1D4B1
-vscr; U+1D4CB
-vsubnE; U+02ACB U+0FE00
-vsubne; U+0228A U+0FE00
-vsupnE; U+02ACC U+0FE00
-vsupne; U+0228B U+0FE00
-Vvdash; U+022AA
-vzigzag; U+0299A
-Wcirc; U+00174
-wcirc; U+00175
-wedbar; U+02A5F
-Wedge; U+022C0
-wedge; U+02227
-wedgeq; U+02259
-weierp; U+02118
-Wfr; U+1D51A
-wfr; U+1D534
-Wopf; U+1D54E
-wopf; U+1D568
-wp; U+02118
-wr; U+02240
-wreath; U+02240
-Wscr; U+1D4B2
-wscr; U+1D4CC
-xcap; U+022C2
-xcirc; U+025EF
-xcup; U+022C3
-xdtri; U+025BD
-Xfr; U+1D51B
-xfr; U+1D535
-xhArr; U+027FA
-xharr; U+027F7
-Xi; U+0039E
-xi; U+003BE
-xlArr; U+027F8
-xlarr; U+027F5
-xmap; U+027FC
-xnis; U+022FB
-xodot; U+02A00
-Xopf; U+1D54F
-xopf; U+1D569
-xoplus; U+02A01
-xotime; U+02A02
-xrArr; U+027F9
-xrarr; U+027F6
-Xscr; U+1D4B3
-xscr; U+1D4CD
-xsqcup; U+02A06
-xuplus; U+02A04
-xutri; U+025B3
-xvee; U+022C1
-xwedge; U+022C0
-Yacute; U+000DD
-Yacute U+000DD
-yacute; U+000FD
-yacute U+000FD
-YAcy; U+0042F
-yacy; U+0044F
-Ycirc; U+00176
-ycirc; U+00177
-Ycy; U+0042B
-ycy; U+0044B
-yen; U+000A5
-yen U+000A5
-Yfr; U+1D51C
-yfr; U+1D536
-YIcy; U+00407
-yicy; U+00457
-Yopf; U+1D550
-yopf; U+1D56A
-Yscr; U+1D4B4
-yscr; U+1D4CE
-YUcy; U+0042E
-yucy; U+0044E
-Yuml; U+00178
-yuml; U+000FF
-yuml U+000FF
-Zacute; U+00179
-zacute; U+0017A
-Zcaron; U+0017D
-zcaron; U+0017E
-Zcy; U+00417
-zcy; U+00437
-Zdot; U+0017B
-zdot; U+0017C
-zeetrf; U+02128
-ZeroWidthSpace; U+0200B
-Zeta; U+00396
-zeta; U+003B6
-Zfr; U+02128
-zfr; U+1D537
-ZHcy; U+00416
-zhcy; U+00436
-zigrarr; U+021DD
-Zopf; U+02124
-zopf; U+1D56B
-Zscr; U+1D4B5
-zscr; U+1D4CF
-zwj; U+0200D
-zwnj; U+0200C
@@ -3,57 +3,27 @@ use Mojo::Base -base;
 
 # "Professor: These old Doomsday devices are dangerously unstable. I'll rest
 #             easier not knowing where they are."
-use Carp 'croak';
+use Carp ();
 use Mojo::Home;
 use Mojo::Log;
 use Mojo::Transaction::HTTP;
 use Mojo::UserAgent;
-use Scalar::Util 'weaken';
+use Mojo::Util;
+use Scalar::Util ();
 
-has home => sub { Mojo::Home->new };
+has home => sub { Mojo::Home->new->detect(ref shift) };
 has log  => sub { Mojo::Log->new };
 has ua   => sub {
-  my $self = shift;
-
   my $ua = Mojo::UserAgent->new;
-  weaken $ua->server->app($self)->{app};
-  weaken $self;
-  return $ua->catch(sub { $self->log->error($_[1]) });
+  Scalar::Util::weaken $ua->server->app(shift)->{app};
+  return $ua;
 };
 
 sub build_tx { Mojo::Transaction::HTTP->new }
 
-sub config { shift->_dict(config => @_) }
-
-sub handler { croak 'Method "handler" not implemented in subclass' }
-
-sub new {
-  my $self = shift->SUPER::new(@_);
-
-  # Check if we have a log directory
-  my $home = $self->home;
-  $home->detect(ref $self) unless @{$home->parts};
-  $self->log->path($home->rel_file('log/mojo.log'))
-    if -w $home->rel_file('log');
-
-  return $self;
-}
-
-sub _dict {
-  my ($self, $name) = (shift, shift);
+sub config { Mojo::Util::_stash(config => @_) }
 
-  # Hash
-  my $dict = $self->{$name} ||= {};
-  return $dict unless @_;
-
-  # Get
-  return $dict->{$_[0]} unless @_ > 1 || ref $_[0];
-
-  # Set
-  %$dict = (%$dict, %{ref $_[0] ? $_[0] : {@_}});
-
-  return $self;
-}
+sub handler { Carp::croak 'Method "handler" not implemented in subclass' }
 
 1;
 
@@ -168,14 +138,6 @@ be overloaded in a subclass.
     ...
   }
 
-=head2 new
-
-  my $app = Mojo->new;
-
-Construct a new L<Mojo> application. Will automatically detect your home
-directory if necessary and set up logging to C<log/mojo.log> if there's a
-C<log> directory.
-
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -9,9 +9,8 @@ has usage => sub { shift->extract_usage };
 
 sub run {
   my ($self, @args) = @_;
-  my $cgi = Mojo::Server::CGI->new(app => $self->app);
-  GetOptionsFromArray \@args, nph => sub { $cgi->nph(1) };
-  $cgi->run;
+  GetOptionsFromArray \@args, nph => \(my $nph = 0);
+  Mojo::Server::CGI->new(app => $self->app, nph => $nph)->run;
 }
 
 1;
@@ -32,7 +32,7 @@ sub run {
     my $msg = $tx->error->{message};
     if    ($code == 401) { $msg = 'Wrong username or password.' }
     elsif ($code == 409) { $msg = 'File already exists on CPAN.' }
-    die qq{Problem uploading file "$file". ($msg)\n};
+    die qq{Problem uploading file "$file": $msg\n};
   }
 
   say 'Upload successful!';
@@ -39,6 +39,7 @@ Mojolicious::Command::daemon - Daemon command
 
     ./myapp.pl daemon -m production -l http://*:8080
     ./myapp.pl daemon -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl daemon -l 'https://*:443?cert=./server.crt&key=./server.key'
 
   Options:
     -b, --backlog <size>         Listen backlog size, defaults to SOMAXCONN.
@@ -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);
 
@@ -47,6 +47,64 @@ EOF
 }
 
 1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::app - App generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate app [NAME]
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::app> generates application directory
+structures for fully functional L<Mojolicious> applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::app> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $app->description;
+  $app            = $app->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $app->usage;
+  $app      = $app->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::app> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $app->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
+
 __DATA__
 
 @@ mojo
@@ -56,8 +114,7 @@ __DATA__
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
+use lib 'lib';
 
 # Start command line interface for application
 require Mojolicious::Commands;
@@ -139,62 +196,3 @@ This page was generated from the template "templates/example/welcome.html.ep"
 and the layout "templates/layouts/default.html.ep",
 <a href="<%%== url_for %>">click here</a> to reload the page or
 <a href="/index.html">here</a> to move forward to a static page.
-
-__END__
-
-=encoding utf8
-
-=head1 NAME
-
-Mojolicious::Command::generate::app - App generator command
-
-=head1 SYNOPSIS
-
-  Usage: APPLICATION generate app [NAME]
-
-=head1 DESCRIPTION
-
-L<Mojolicious::Command::generate::app> generates application directory
-structures for fully functional L<Mojolicious> applications.
-
-This is a core command, that means it is always enabled and its code a good
-example for learning to build new commands, you're welcome to fork it.
-
-See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
-available by default.
-
-=head1 ATTRIBUTES
-
-L<Mojolicious::Command::generate::app> inherits all attributes from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 description
-
-  my $description = $app->description;
-  $app            = $app->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 usage
-
-  my $usage = $app->usage;
-  $app      = $app->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojolicious::Command::generate::app> inherits all methods from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 run
-
-  $app->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
-
-=cut
@@ -8,40 +8,10 @@ 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;
-__DATA__
-
-@@ liteapp
-#!/usr/bin/env perl
-use Mojolicious::Lite;
-
-# Documentation browser under "/perldoc"
-plugin 'PODRenderer';
-
-get '/' => sub {
-  my $c = shift;
-  $c->render('index');
-};
-
-app->start;
-<% %>__DATA__
-
-<% %>@@ index.html.ep
-%% layout 'default';
-%% title 'Welcome';
-Welcome to the Mojolicious real-time web framework!
-
-<% %>@@ layouts/default.html.ep
-<!DOCTYPE html>
-<html>
-  <head><title><%%= title %></title></head>
-  <body><%%= content %></body>
-</html>
-
-__END__
 
 =encoding utf8
 
@@ -99,3 +69,32 @@ Run this command.
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+
+@@ liteapp
+#!/usr/bin/env perl
+use Mojolicious::Lite;
+
+# Documentation browser under "/perldoc"
+plugin 'PODRenderer';
+
+get '/' => sub {
+  my $c = shift;
+  $c->render('index');
+};
+
+app->start;
+<% %>__DATA__
+
+<% %>@@ index.html.ep
+%% layout 'default';
+%% title 'Welcome';
+Welcome to the Mojolicious real-time web framework!
+
+<% %>@@ layouts/default.html.ep
+<!DOCTYPE html>
+<html>
+  <head><title><%%= title %></title></head>
+  <body><%%= content %></body>
+</html>
@@ -9,21 +9,6 @@ has usage => sub { shift->extract_usage };
 sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') }
 
 1;
-__DATA__
-
-@@ makefile
-use strict;
-use warnings;
-
-use ExtUtils::MakeMaker;
-
-WriteMakefile(
-  VERSION   => '0.01',
-  PREREQ_PM => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
-  test      => {TESTS => 't/*.t'}
-);
-
-__END__
 
 =encoding utf8
 
@@ -81,3 +66,17 @@ Run this command.
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+
+@@ makefile
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+  VERSION   => '0.01',
+  PREREQ_PM => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
+  test      => {TESTS => 't/*.t'}
+);
@@ -26,6 +26,64 @@ sub run {
 }
 
 1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::plugin - Plugin generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate plugin [NAME]
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::plugin> generates directory structures for
+fully functional L<Mojolicious> plugins.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::plugin> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $plugin->description;
+  $plugin         = $plugin->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $plugin->usage;
+  $plugin   = $plugin->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::plugin> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $plugin->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
+
 __DATA__
 
 @@ class
@@ -111,62 +169,3 @@ WriteMakefile(
   PREREQ_PM    => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
   test         => {TESTS => 't/*.t'}
 );
-
-__END__
-
-=encoding utf8
-
-=head1 NAME
-
-Mojolicious::Command::generate::plugin - Plugin generator command
-
-=head1 SYNOPSIS
-
-  Usage: APPLICATION generate plugin [NAME]
-
-=head1 DESCRIPTION
-
-L<Mojolicious::Command::generate::plugin> generates directory structures for
-fully functional L<Mojolicious> plugins.
-
-This is a core command, that means it is always enabled and its code a good
-example for learning to build new commands, you're welcome to fork it.
-
-See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
-available by default.
-
-=head1 ATTRIBUTES
-
-L<Mojolicious::Command::generate::plugin> inherits all attributes from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 description
-
-  my $description = $plugin->description;
-  $plugin         = $plugin->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 usage
-
-  my $usage = $plugin->usage;
-  $plugin   = $plugin->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojolicious::Command::generate::plugin> inherits all methods from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 run
-
-  $plugin->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
-
-=cut
@@ -29,8 +29,7 @@ sub run {
   my $selector = shift @args;
 
   # Parse header pairs
-  my %headers;
-  /^\s*([^:]+)\s*:\s*(.+)$/ and $headers{$1} = $2 for @headers;
+  my %headers = map { /^\s*([^:]+)\s*:\s*(.+)$/ ? ($1, $2) : () } @headers;
 
   # Detect proxy for absolute URLs
   my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
@@ -66,14 +65,12 @@ sub run {
   STDOUT->autoflush(1);
   my $tx = $ua->start($ua->build_tx($method, $url, \%headers, $content));
   my $err = $tx->error;
-  $url = encode 'UTF-8', $url;
-  warn qq{Problem loading URL "$url". ($err->{message})\n}
+  warn qq{Problem loading URL "@{[$tx->req->url]}": $err->{message}\n}
     if $err && !$err->{code};
 
   # JSON Pointer
   return unless defined $selector;
-  my $type = $tx->res->headers->content_type // '';
-  return _json($buffer, $selector) if $type =~ /json/i;
+  return _json($buffer, $selector) if $selector eq '' || $selector =~ m!^/!;
 
   # Selector
   _select($buffer, $selector, $charset // $tx->res->content->charset, @args);
@@ -102,13 +99,14 @@ sub _select {
     ($results = $results->slice($command)) and next if $command =~ /^\d+$/;
 
     # Text
-    return _say($results->text->each) if $command eq 'text';
+    return _say($results->map('text')->each) if $command eq 'text';
 
     # All text
-    return _say($results->all_text->each) if $command eq 'all';
+    return _say($results->map('all_text')->each) if $command eq 'all';
 
     # Attribute
-    return _say($results->attr($args[0] // '')->each) if $command eq 'attr';
+    return _say($results->map(attr => $args[0] // '')->each)
+      if $command eq 'attr';
 
     # Unknown
     die qq{Unknown command "$command".\n};
@@ -132,8 +130,8 @@ 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 -M POST -c 'trololo' mojolicio.us
+    mojo get -v -H 'Host: mojolicious.org' -H 'Accept: */*' mojolicio.us
+    mojo get -M POST -H 'Content-Type: text/trololo' -c 'trololo' mojolicio.us
     mojo get mojolicio.us 'head > title' text
     mojo get mojolicio.us .footer all
     mojo get mojolicio.us a attr href
@@ -23,7 +23,7 @@ sub run {
   }
 
   # Turn them into real files
-  for my $name (keys %all) {
+  for my $name (grep {/\.\w+$/} keys %all) {
     my $prefix = $name =~ /\.\w+\.\w+$/ ? 'templates' : 'public';
     $self->write_file($self->rel_file("$prefix/$name"), $all{$name});
   }
@@ -50,6 +50,7 @@ Mojolicious::Command::prefork - Prefork command
 
     ./myapp.pl prefork -m production -l http://*:8080
     ./myapp.pl prefork -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl prefork -l 'https://*:443?cert=./server.crt&key=./server.key'
 
   Options:
     -A, --accepts <number>               Number of connections for workers to
@@ -28,9 +28,9 @@ sub _walk {
 
   # Flags
   my @flags;
-  push @flags, $route->inline ? 'B' : '.';
   push @flags, @{$route->over || []} ? 'C' : '.';
   push @flags, (my $partial = $route->partial) ? 'D' : '.';
+  push @flags, $route->inline       ? 'U' : '.';
   push @flags, $route->is_websocket ? 'W' : '.';
   push @$row, join('', @flags) if $verbose;
 
@@ -44,13 +44,10 @@ sub _walk {
 
   # Regex (verbose)
   my $pattern = $route->pattern;
-  $pattern->match('/', $route->is_endpoint);
-  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;
+  $pattern->match('/', $route->is_endpoint && !$partial);
+  my $regex  = (regexp_pattern $pattern->regex)[0];
+  my $format = (regexp_pattern($pattern->format_regex))[0];
+  push @$row, $regex, $format ? $format : '' if $verbose;
 
   $depth++;
   _walk($_, $depth, $rows, $verbose) for @{$route->children};
@@ -71,7 +68,7 @@ Mojolicious::Command::routes - Routes command
 
   Options:
     -v, --verbose   Print additional details about routes, flags indicate
-                    B=Bridge, C=Conditions, D=Detour and W=WebSocket.
+                    C=Conditions, D=Detour, U=Under and W=WebSocket.
 
 =head1 DESCRIPTION
 
@@ -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 };
@@ -13,21 +9,13 @@ has usage => sub { shift->extract_usage };
 sub run {
   my ($self, @args) = @_;
 
-  GetOptionsFromArray \@args, 'v|verbose' => sub { $ENV{HARNESS_VERBOSE} = 1 };
+  GetOptionsFromArray \@args, 'v|verbose' => \$ENV{HARNESS_VERBOSE};
 
-  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';
@@ -47,9 +35,9 @@ Mojolicious::Command::test - Test command
 
   Usage: APPLICATION test [OPTIONS] [TESTS]
 
-    ./myapp.pl test -v
+    ./myapp.pl test
     ./myapp.pl test t/foo.t
-    ./myapp.pl test t/foo/*.t
+    ./myapp.pl test -v t/foo/*.t
 
   Options:
     -v, --verbose   Print verbose debug information to STDERR.
@@ -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 $socks = $class->SOCKS ? $IO::Socket::Socks::VERSION : 'not installed';
+  my $tls   = $class->TLS ? $IO::Socket::SSL::VERSION : 'not installed';
+  my $ndn   = $class->NDN ? $Net::DNS::Native::VERSION : 'not installed';
 
   print <<EOF;
 CORE
@@ -23,20 +23,19 @@ 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::Socks 0.64+ ($socks)
+  IO::Socket::SSL 1.84+   ($tls)
+  Net::DNS::Native 0.14+  ($ndn)
 
 EOF
 
   # Check latest version on CPAN
   my $latest = eval {
-    my $ua = Mojo::UserAgent->new(max_redirects => 10);
-    $ua->proxy->detect;
-    $ua->get('api.metacpan.org/v0/release/Mojolicious')->res->json->{version};
-  };
+    Mojo::UserAgent->new(max_redirects => 10)->tap(sub { $_->proxy->detect })
+      ->get('api.metacpan.org/v0/release/Mojolicious')->res->json->{version};
+  } or return;
 
-  return unless $latest;
   my $msg = 'This version is up to date, have fun!';
   $msg = 'Thanks for testing a development release, you are awesome!'
     if $latest < $Mojolicious::VERSION;
@@ -14,14 +14,13 @@ use Pod::Usage 'pod2usage';
 
 has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') };
 has description => 'No description.';
-has quiet       => 0;
-has usage       => "Usage: APPLICATION\n";
+has 'quiet';
+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;
 }
 
@@ -131,7 +130,7 @@ L<Mojolicious::Command> implements the following attributes.
 =head2 app
 
   my $app  = $command->app;
-  $command = $command->app(MyApp->new);
+  $command = $command->app(Mojolicious->new);
 
 Application for command, defaults to a L<Mojo::HelloWorld> object.
 
@@ -1,7 +1,7 @@
 package Mojolicious::Commands;
 use Mojo::Base 'Mojolicious::Command';
 
-use Getopt::Long 'GetOptions';
+use Getopt::Long 'GetOptionsFromArray';
 use Mojo::Server;
 use Mojo::Util 'tablify';
 
@@ -26,16 +26,20 @@ sub detect {
 }
 
 # Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
-BEGIN {
-  Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case pass_through));
-  GetOptions(
-    'h|help'   => sub { $ENV{MOJO_HELP} = 1 },
-    'home=s'   => sub { $ENV{MOJO_HOME} = $_[1] },
-    'm|mode=s' => sub { $ENV{MOJO_MODE} = $_[1] }
-  ) unless __PACKAGE__->detect;
-  Getopt::Long::Configure('default');
+sub _args {
+  return if __PACKAGE__->detect;
+
+  my $save
+    = Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case pass_through));
+  GetOptionsFromArray shift,
+    'h|help'   => \$ENV{MOJO_HELP},
+    'home=s'   => \$ENV{MOJO_HOME},
+    'm|mode=s' => \$ENV{MOJO_MODE};
+  Getopt::Long::Configure($save);
 }
 
+BEGIN { _args([@ARGV]) }
+
 sub run {
   my ($self, $name, @args) = @_;
 
@@ -52,6 +56,8 @@ sub run {
     $name = shift @args if my $help = $name eq 'help';
     $help = $ENV{MOJO_HELP} = $ENV{MOJO_HELP} ? 1 : $help;
 
+    # Remove options shared by all commands before loading the command
+    _args(\@args);
     my $module;
     $module = _command("${_}::$name", 1) and last for @{$self->namespaces};
 
@@ -83,10 +89,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) = @_;
@@ -313,7 +316,7 @@ disabled with the C<MOJO_NO_DETECT> environment variable.
   Mojolicious::Commands->start_app('MyApp');
   Mojolicious::Commands->start_app(MyApp => @ARGV);
 
-Load application and start the command line interface for it.
+Load application from class and start the command line interface for it.
 
   # Always start daemon for application and ignore @ARGV
   Mojolicious::Commands->start_app('MyApp', 'daemon', '-l', 'http://*:8080');
@@ -4,7 +4,6 @@ use Mojo::Base -base;
 # No imports, for security reasons!
 use Carp ();
 use Mojo::ByteStream;
-use Mojo::Exception;
 use Mojo::Transaction::HTTP;
 use Mojo::URL;
 use Mojo::Util;
@@ -27,13 +26,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(@_);
 }
 
@@ -43,7 +42,7 @@ sub cookie {
   my ($self, $name) = (shift, shift);
 
   # Multiple names
-  return map { scalar $self->cookie($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->cookie($_) } @$name if ref $name eq 'ARRAY';
 
   # Response cookie
   if (@_) {
@@ -58,11 +57,18 @@ sub cookie {
   }
 
   # Request cookies
-  return map { $_->value } $self->req->cookie($name) if wantarray;
   return undef unless my $cookie = $self->req->cookie($name);
   return $cookie->value;
 }
 
+sub every_cookie {
+  [map { $_->value } @{shift->req->every_cookie(shift)}];
+}
+
+sub every_param { _param(@_) }
+
+sub every_signed_cookie { _signed_cookie(@_) }
+
 sub finish {
   my $self = shift;
 
@@ -87,12 +93,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;
@@ -104,7 +112,7 @@ sub param {
   my ($self, $name) = (shift, shift);
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
   # List names
   my $captures = $self->stash->{'mojo.captures'} ||= {};
@@ -117,22 +125,12 @@ sub param {
     return sort @keys;
   }
 
-  # Override values
-  if (@_) {
-    $captures->{$name} = @_ > 1 ? [@_] : $_[0];
-    return $self;
-  }
-
-  # Captured unreserved values
-  if (!$RESERVED{$name} && defined(my $value = $captures->{$name})) {
-    return ref $value eq 'ARRAY' ? wantarray ? @$value : $$value[0] : $value;
-  }
+  # Value
+  return _param($self, $name)->[-1] unless @_;
 
-  # Uploads
-  return $req->upload($name) if $req->upload($name);
-
-  # Param values
-  return $req->param($name);
+  # Override values
+  $captures->{$name} = @_ > 1 ? [@_] : $_[0];
+  return $self;
 }
 
 sub redirect_to {
@@ -170,37 +168,29 @@ sub render {
   return !!$self->rendered($self->stash->{status});
 }
 
-sub render_exception { _development('exception', @_) }
+sub render_exception { shift->helpers->reply->exception(@_) }
 
 sub render_later { shift->stash('mojo.rendered' => 1) }
 
 sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) }
 
-sub render_not_found { _development('not_found', @_) }
-
-sub render_static {
-  my ($self, $file) = @_;
-  my $app = $self->app;
-  return !!$self->rendered if $app->static->serve($self, $file);
-  $app->log->debug(qq{File "$file" not found, public directory missing?});
-  return !$self->render_not_found;
-}
+sub render_not_found { shift->helpers->reply->not_found }
 
 sub render_to_string { shift->render(@_, 'mojo.to_string' => 1) }
 
 sub rendered {
   my ($self, $status) = @_;
 
-  # Disable auto rendering and make sure we have a status
-  my $res = $self->render_later->res;
+  # Make sure we have a status
+  my $res = $self->res;
   $res->code($status || 200) if $status || !$res->code;
 
   # Finish transaction
   my $stash = $self->stash;
-  unless ($stash->{'mojo.finished'}++) {
+  if (!$stash->{'mojo.finished'} && ++$stash->{'mojo.finished'}) {
 
-    # Stop timer
-    my $app = $self->app;
+    # Disable auto rendering and stop timer
+    my $app = $self->render_later->app;
     if (my $started = delete $stash->{'mojo.started'}) {
       my $elapsed = sprintf '%f',
         Time::HiRes::tv_interval($started, [Time::HiRes::gettimeofday()]);
@@ -252,21 +242,26 @@ sub send {
   Carp::croak 'No WebSocket connection to send message to'
     unless $tx->is_websocket;
   $tx->send($msg, $cb ? sub { shift; $self->$cb(@_) } : ());
-  return $self->rendered(101);
+  return $self;
 }
 
 sub session {
   my $self = shift;
 
+  my $stash = $self->stash;
+  $self->app->sessions->load($self)
+    unless exists $stash->{'mojo.active_session'};
+
   # Hash
-  my $session = $self->stash->{'mojo.session'} ||= {};
+  my $session = $stash->{'mojo.session'} ||= {};
   return $session unless @_;
 
   # Get
   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;
 }
@@ -275,43 +270,18 @@ sub signed_cookie {
   my ($self, $name, $value, $options) = @_;
 
   # Multiple names
-  return map { scalar $self->signed_cookie($_) } @$name
-    if ref $name eq 'ARRAY';
-
-  # Response cookie
-  my $secrets = $self->stash->{'mojo.secrets'};
-  return $self->cookie($name,
-    "$value--" . Mojo::Util::hmac_sha1_sum($value, $secrets->[0]), $options)
-    if defined $value;
+  return map { $self->signed_cookie($_) } @$name if ref $name eq 'ARRAY';
 
-  # Request cookies
-  my @results;
-  for my $value ($self->cookie($name)) {
-
-    # Check signature with rotating secrets
-    if ($value =~ s/--([^\-]+)$//) {
-      my $signature = $1;
-
-      my $valid;
-      for my $secret (@$secrets) {
-        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
-        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
-      }
-      if ($valid) { push @results, $value }
+  # Request cookie
+  return _signed_cookie($self, $name)->[-1] unless defined $value;
 
-      else {
-        $self->app->log->debug(
-          qq{Bad signed cookie "$name", possible hacking attempt.});
-      }
-    }
-
-    else { $self->app->log->debug(qq{Cookie "$name" not signed.}) }
-  }
-
-  return wantarray ? @results : $results[0];
+  # Response cookie
+  my $checksum
+    = Mojo::Util::hmac_sha1_sum($value, $self->stash->{'mojo.secrets'}[0]);
+  return $self->cookie($name, "$value--$checksum", $options);
 }
 
-sub stash { shift->Mojolicious::_dict(stash => @_) }
+sub stash { Mojo::Util::_stash(stash => @_) }
 
 sub url_for {
   my $self = shift;
@@ -383,48 +353,46 @@ sub write_chunk {
   return $self->rendered;
 }
 
-sub _development {
-  my ($page, $self, $e) = @_;
+sub _param {
+  my ($self, $name) = @_;
 
-  my $app = $self->app;
-  $app->log->error($e = Mojo::Exception->new($e)) if $page eq 'exception';
+  # Captured unreserved values
+  my $captures = $self->stash->{'mojo.captures'} ||= {};
+  if (!$RESERVED{$name} && defined(my $value = $captures->{$name})) {
+    return ref $value eq 'ARRAY' ? $value : [$value];
+  }
 
-  # Filtered stash snapshot
-  my $stash = $self->stash;
-  my %snapshot = map { $_ => $stash->{$_} }
-    grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
-
-  # Render with fallbacks
-  my $mode     = $app->mode;
-  my $renderer = $app->renderer;
-  my $options  = {
-    exception => $page eq 'exception' ? $e : undef,
-    format => $stash->{format} || $renderer->default_format,
-    handler  => undef,
-    snapshot => \%snapshot,
-    status   => $page eq 'exception' ? 500 : 404,
-    template => "$page.$mode"
-  };
-  my $inline = $renderer->_bundled($mode eq 'development' ? $mode : $page);
-  return $self if _fallbacks($self, $options, $page, $inline);
-  _fallbacks($self, {%$options, format => 'html'}, $page, $inline);
-  return $self;
+  # Uploads or param values
+  my $req     = $self->req;
+  my $uploads = $req->every_upload($name);
+  return @$uploads ? $uploads : $req->every_param($name);
 }
 
-sub _fallbacks {
-  my ($self, $options, $template, $inline) = @_;
+sub _signed_cookie {
+  my ($self, $name) = @_;
 
-  # Mode specific template
-  return 1 if $self->render_maybe(%$options);
+  my $secrets = $self->stash->{'mojo.secrets'};
+  my @results;
+  for my $value (@{$self->every_cookie($name)}) {
 
-  # Normal template
-  return 1 if $self->render_maybe(%$options, template => $template);
+    # Check signature with rotating secrets
+    if ($value =~ s/--([^\-]+)$//) {
+      my $signature = $1;
 
-  # Inline template
-  my $stash = $self->stash;
-  return undef unless $stash->{format} eq 'html';
-  delete @$stash{qw(extends layout)};
-  return $self->render_maybe(%$options, inline => $inline, handler => 'ep');
+      my $valid;
+      for my $secret (@$secrets) {
+        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
+        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
+      }
+      if ($valid) { push @results, $value }
+
+      else { $self->app->log->debug(qq{Cookie "$name" has bad signature.}) }
+    }
+
+    else { $self->app->log->debug(qq{Cookie "$name" not signed.}) }
+  }
+
+  return \@results;
 }
 
 1;
@@ -438,7 +406,7 @@ Mojolicious::Controller - Controller base class
 =head1 SYNOPSIS
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -483,8 +451,8 @@ Router results for the current request, defaults to a
 L<Mojolicious::Routes::Match> object.
 
   # Introspect
-  my $foo = $c->match->endpoint->pattern->defaults->{foo};
-  my $bar = $c->match->stack->[-1]{bar};
+  my $controller = $c->match->endpoint->pattern->defaults->{controller};
+  my $action     = $c->match->stack->[-1]{action};
 
 =head2 tx
 
@@ -520,17 +488,48 @@ Continue dispatch chain with L<Mojolicious::Routes/"continue">.
 
 =head2 cookie
 
-  my $foo         = $c->cookie('foo');
-  my @foo         = $c->cookie('foo');
+  my $value       = $c->cookie('foo');
   my ($foo, $bar) = $c->cookie(['foo', 'bar']);
   $c              = $c->cookie(foo => 'bar');
   $c              = $c->cookie(foo => 'bar', {path => '/'});
 
-Access request cookie values and create new response cookies.
+Access request cookie values and create new response cookies. If there are
+multiple values sharing the same name, and you want to access more than just
+the last one, you can use L</"every_cookie">.
 
   # Create response cookie with domain and expiration date
   $c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60});
 
+=head2 every_cookie
+
+  my $values = $c->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all request cookie values sharing the
+same name as an array reference.
+
+  $ Get first cookie value
+  my $first = $c->every_cookie('foo')->[0];
+
+=head2 every_param
+
+  my $values = $c->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
+
+=head2 every_signed_cookie
+
+  my $values = $c->every_signed_cookie('foo');
+
+Similar to L</"signed_cookie">, but returns all signed request cookie values
+sharing the same name as an array reference.
+
+  # Get first signed cookie value
+  my $first = $c->every_signed_cookie('foo')->[0];
+
 =head2 finish
 
   $c = $c->finish;
@@ -553,6 +552,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 {...});
@@ -590,30 +600,23 @@ status.
 =head2 param
 
   my @names       = $c->param;
-  my $foo         = $c->param('foo');
-  my @foo         = $c->param('foo');
+  my $value       = $c->param('foo');
   my ($foo, $bar) = $c->param(['foo', 'bar']);
   $c              = $c->param(foo => 'ba;r');
   $c              = $c->param(foo => qw(ba;r baz));
   $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. If there are multiple values sharing the same
+name, and you want to access more than just the last one, you can use
+L</"every_param">. 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"
-  my $hash = {foo => $c->param('foo')};
-
-  # Better enforce scalar context
-  my $hash = {foo => scalar $c->param('foo')};
-
-  # The multi-name form can also be used to enforce scalar context
-  my $hash = {foo => $c->param(['foo'])};
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
 
 For more control you can also access request information directly.
 
@@ -654,11 +657,11 @@ Prepare a C<302> redirect response, takes the same arguments as L</"url_for">.
   my $bool = $c->render(handler => 'something');
   my $bool = $c->render('foo/index');
 
-Render content using L<Mojolicious::Renderer/"render"> and emit hooks
-L<Mojolicious/"before_render"> as well as L<Mojolicious/"after_render">. If no
-template is provided a default one based on controller and action or route
-name will be generated with L<Mojolicious::Renderer/"template_for">, all
-additional pairs get merged into the L</"stash">.
+Render content with L<Mojolicious::Renderer/"render"> and emit hooks
+L<Mojolicious/"before_render"> as well as L<Mojolicious/"after_render">, or
+call L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found"> if no
+response could be generated, all additional pairs get merged into the
+L</"stash">.
 
   # Render characters
   $c->render(text => 'I ♥ Mojolicious!');
@@ -684,10 +687,7 @@ additional pairs get merged into the L</"stash">.
   $c = $c->render_exception('Oops!');
   $c = $c->render_exception(Mojo::Exception->new('Oops!'));
 
-Render the exception template C<exception.$mode.$format.*> or
-C<exception.$format.*> and set the response status code to C<500>. Also sets
-the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot>
-to a copy of the L</"stash"> for use in the templates.
+Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception">.
 
 =head2 render_later
 
@@ -708,7 +708,8 @@ automatic rendering would result in a response.
   my $bool = $c->render_maybe(controller => 'foo', action => 'bar');
   my $bool = $c->render_maybe('foo/index', format => 'html');
 
-Try to render content, but do not call L</"render_not_found"> if no response
+Try to render content, but do not call
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found"> if no response
 could be generated, takes the same arguments as L</"render">.
 
   # Render template "index_local" only if it exists
@@ -718,19 +719,7 @@ could be generated, takes the same arguments as L</"render">.
 
   $c = $c->render_not_found;
 
-Render the not found template C<not_found.$mode.$format.*> or
-C<not_found.$format.*> and set the response status code to C<404>. Also sets
-the stash value C<snapshot> to a copy of the L</"stash"> for use in the
-templates.
-
-=head2 render_static
-
-  my $bool = $c->render_static('images/logo.png');
-  my $bool = $c->render_static('../lib/MyApp.pm');
-
-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.
+Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
 
 =head2 render_to_string
 
@@ -761,7 +750,7 @@ using a C<200> response code.
 
   my $req = $c->req;
 
-Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">.
+Get L<Mojo::Message::Request> object from L</"tx">.
 
   # Longer version
   my $req = $c->tx->req;
@@ -783,7 +772,7 @@ Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">.
 
   my $res = $c->res;
 
-Get L<Mojo::Message::Response> object from L<Mojo::Transaction/"res">.
+Get L<Mojo::Message::Response> object from L</"tx">.
 
   # Longer version
   my $res = $c->tx->res;
@@ -825,9 +814,7 @@ L<Mojolicious::Plugin::DefaultHelpers/"accepts">.
   $c = $c->send($chars => sub {...});
 
 Send message or frame non-blocking via WebSocket, the optional drain callback
-will be invoked once all data has been written. Note that this method will
-automatically respond to WebSocket handshake requests with a C<101> response
-status.
+will be invoked once all data has been written.
 
   # Send "Text" message
   $c->send('I ♥ Mojolicious!');
@@ -883,15 +870,15 @@ on browser.
 
 =head2 signed_cookie
 
-  my $foo         = $c->signed_cookie('foo');
-  my @foo         = $c->signed_cookie('foo');
+  my $value       = $c->signed_cookie('foo');
   my ($foo, $bar) = $c->signed_cookie(['foo', 'bar']);
   $c              = $c->signed_cookie(foo => 'bar');
   $c              = $c->signed_cookie(foo => 'bar', {path => '/'});
 
-Access signed request cookie values and create new signed response cookies.
-Cookies failing HMAC-SHA1 signature verification will be automatically
-discarded.
+Access signed request cookie values and create new signed response cookies. If
+there are multiple values sharing the same name, and you want to access more
+than just the last one, you can use L</"every_signed_cookie">. Cookies failing
+HMAC-SHA1 signature verification will be automatically discarded.
 
 =head2 stash
 
@@ -945,9 +932,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);
@@ -1022,9 +1011,13 @@ helpers provided by L</"app"> on L<Mojolicious::Controller> objects. This
 includes all helpers from L<Mojolicious::Plugin::DefaultHelpers> and
 L<Mojolicious::Plugin::TagHelpers>.
 
+  # Call helpers
   $c->layout('green');
   $c->title('Welcome!');
 
+  # Longer version
+  $c->helpers->layout('green');
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -38,16 +38,6 @@ Please report security issues directly to the CPAN email address of the
 pumpkin-holder, which is currently C<sri@cpan.org>, and give us a few days to
 develop and release a proper fix.
 
-=head2 Feature requests
-
-Please do not open GitHub issues for feature requests, if there's something
-you would like to see in a future version of L<Mojolicious>, you have to write
-the code yourself.
-
-If you're looking for feedback on your ideas, you're welcome to discuss them
-on the L<mailing-list|http://groups.google.com/group/mojolicious> or the
-official IRC channel C<#mojo> on C<irc.perl.org>.
-
 =head1 RESOLVING ISSUES
 
 There are many ways in which you can help us resolve existing issues on the
@@ -136,8 +126,8 @@ It's not a feature without a test and documentation.
 
 A feature is only needed when the majority of the user base benefits from it.
 
-Features may only be changed in a major release or after being deprecated for
-at least 3 months.
+Features may only be changed in a major release, to fix a serious security
+issue, or after being deprecated for at least 3 months.
 
 Refactoring and deprecations should be avoided if no important feature depends
 on it.
@@ -23,7 +23,7 @@ server with L<Mojo::Server::Daemon>. It is usually used during development and
 in the construction of more advanced web servers, but is solid and fast enough
 for small to mid sized applications.
 
-  $ ./script/myapp daemon
+  $ ./script/my_app daemon
   Server available at http://127.0.0.1:3000.
 
 It is available to every application through the command
@@ -31,7 +31,7 @@ L<Mojolicious::Command::daemon>, which has many configuration options and is
 known to work on every platform Perl works on with its single-process
 architecture.
 
-  $ ./script/myapp daemon -h
+  $ ./script/my_app daemon -h
   ...List of available options...
 
 Another huge advantage is that it supports TLS and WebSockets out of the box,
@@ -39,14 +39,14 @@ a development certificate for testing purposes is built right in, so it just
 works, but you can specify all listen locations supported by
 L<Mojo::Server::Daemon/"listen">.
 
-  $ ./script/myapp daemon -l https://[::]:3000
+  $ ./script/my_app daemon -l https://[::]:3000
   Server available at https://[::]:3000.
 
 On UNIX platforms you can also add preforking and switch to a multi-process
 architecture with L<Mojolicious::Command::prefork>, to take advantage of
 multiple CPU cores and copy-on-write.
 
-  $ ./script/myapp prefork
+  $ ./script/my_app prefork
   Server available at http://127.0.0.1:3000.
 
 Since all built-in web servers are based on the L<Mojo::IOLoop> event loop,
@@ -56,7 +56,7 @@ by increasing the number of worker processes and decreasing the number of
 concurrent connections each worker is allowed to handle (often as low as
 C<1>).
 
-  $ ./script/myapp prefork -m production -w 10 -c 1
+  $ ./script/my_app prefork -m production -w 10 -c 1
   Server available at http://127.0.0.1:3000.
 
 Your application is preloaded in the manager process during startup, to run
@@ -85,7 +85,7 @@ It is basically a restarter that forks a new L<Mojo::Server::Daemon> web
 server whenever a file in your project changes, and should therefore only be
 used during development.
 
-  $ morbo script/myapp
+  $ morbo ./script/my_app
   Server available at http://127.0.0.1:3000.
 
 =head2 Hypnotoad
@@ -105,12 +105,12 @@ It is based on the L<Mojo::Server::Prefork> web server, which adds preforking
 to L<Mojo::Server::Daemon>, but optimized specifically for production
 environments out of the box.
 
-  $ hypnotoad script/myapp
+  $ hypnotoad ./script/my_app
   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;
 
@@ -136,7 +136,7 @@ software upgrades (hot deployment). That means you can upgrade L<Mojolicious>,
 Perl or even system libraries at runtime without ever stopping the server or
 losing a single incoming connection, just by running the command above again.
 
-  $ hypnotoad script/myapp
+  $ hypnotoad ./script/my_app
   Starting hot deployment for Hypnotoad server 31841.
 
 You might also want to enable proxy support if you're using Hypnotoad behind a
@@ -153,13 +153,13 @@ as you can see above, but on modern operating systems that support the
 C<SO_REUSEPORT> socket option, there is also another method available that
 works with all built-in web servers.
 
-  $ ./script/myapp prefork -P /tmp/first.pid -l http://*:8080?reuse=1
+  $ ./script/my_app prefork -P /tmp/first.pid -l http://*:8080?reuse=1
   Server available at http://127.0.0.1:8080.
 
 All you have to do is start a second web server listening to the same port and
 stop the first web server gracefully afterwards.
 
-  $ ./script/myapp prefork -P /tmp/second.pid -l http://*:8080?reuse=1
+  $ ./script/my_app prefork -P /tmp/second.pid -l http://*:8080?reuse=1
   Server available at http://127.0.0.1:8080.
   $ kill -s TERM `cat /tmp/first.pid`
 
@@ -215,7 +215,7 @@ production environments is discouraged though, because as a result of how
 C<CGI> works, it is very slow and many web servers are making it exceptionally
 hard to configure properly.
 
-  ScriptAlias / /home/sri/myapp/script/myapp/
+  ScriptAlias / /home/sri/my_app/script/my_app/
 
 =head2 PSGI/Plack
 
@@ -225,23 +225,23 @@ helpers and adapters to web servers. L<PSGI> and L<Plack> are inspired by
 Python's WSGI and Ruby's Rack. L<Mojolicious> applications are ridiculously
 simple to deploy with L<Plack>.
 
-  $ plackup ./script/myapp
+  $ plackup ./script/my_app
 
 L<Plack> provides many server and protocol adapters for you to choose from,
 such as C<FCGI>, C<uWSGI> and C<mod_perl>.
 
-  $ plackup ./script/myapp -s FCGI -l /tmp/myapp.sock
+  $ plackup ./script/my_app -s FCGI -l /tmp/myapp.sock
 
 The C<MOJO_REVERSE_PROXY> environment variable can be used to enable proxy
 support, this allows L<Mojolicious> to automatically pick up the
 C<X-Forwarded-For> and C<X-Forwarded-Proto> headers.
 
-  $ MOJO_REVERSE_PROXY=1 plackup ./script/myapp
+  $ MOJO_REVERSE_PROXY=1 plackup ./script/my_app
 
-If an older server adapter is not be able to correctly detect the application
-home directory, you can simply use the C<MOJO_HOME> environment variable.
+If an older server adapter is unable to correctly detect the application home
+directory, you can simply use the C<MOJO_HOME> environment variable.
 
-  $ MOJO_HOME=/home/sri/myapp plackup ./script/myapp
+  $ MOJO_HOME=/home/sri/my_app plackup ./script/my_app
 
 There is no need for a C<.psgi> file, just point the server adapter at your
 application script, it will automatically act like one if it detects the
@@ -257,7 +257,22 @@ application logic.
 
   builder {
     enable 'Deflater';
-    require 'myapp.pl';
+    require './script/my_app';
+  };
+
+L<Mojo::Server::PSGI> can be used directly to load and customize applications
+in the wrapper script.
+
+  #!/usr/bin/env plackup -s FCGI
+  use Mojo::Server::PSGI;
+  use Plack::Builder;
+
+  builder {
+    enable 'Deflater';
+    my $server = Mojo::Server::PSGI->new;
+    $server->load_app('./script/my_app');
+    $server->app->config(foo => 'bar');
+    $server->to_psgi_app;
   };
 
 But you could even use middleware right in your application.
@@ -284,7 +299,7 @@ you can use the hook L<Mojolicious/"before_dispatch"> to rewrite incoming
 requests.
 
   # Change scheme if "X-Forwarded-HTTPS" header is set
-  app->hook(before_dispatch => sub {
+  $app->hook(before_dispatch => sub {
     my $c = shift;
     $c->req->url->base->scheme('https')
       if $c->req->headers->header('X-Forwarded-HTTPS');
@@ -295,11 +310,11 @@ prefixes your application might be deployed under, rewriting the base path of
 incoming requests is also quite common.
 
   # Move first part and slash from path to base path in production mode
-  app->hook(before_dispatch => sub {
+  $app->hook(before_dispatch => sub {
     my $c = shift;
     push @{$c->req->url->base->path->trailing_slash(1)},
       shift @{$c->req->url->path->leading_slash(0)};
-  }) if app->mode eq 'production';
+  }) if $app->mode eq 'production';
 
 L<Mojo::URL> objects are very easy to manipulate, just make sure that the URL
 (C<foo/bar?baz=yada>), which represents the routing destination, is always
@@ -404,7 +419,7 @@ style.
   use Mojolicious::Lite;
   use Mojo::URL;
 
-  # Search MetaCPAN for "mojo" and "mango"
+  # Search MetaCPAN for "mojo" and "minion"
   get '/' => sub {
     my $c = shift;
 
@@ -416,16 +431,16 @@ style.
         my $delay = shift;
         my $url   = Mojo::URL->new('api.metacpan.org/v0/module/_search');
         $url->query({sort => 'date:desc'});
-        $c->ua->get($url->clone->query({q => 'mojo'})  => $delay->begin);
-        $c->ua->get($url->clone->query({q => 'mango'}) => $delay->begin);
+        $c->ua->get($url->clone->query({q => 'mojo'})   => $delay->begin);
+        $c->ua->get($url->clone->query({q => 'minion'}) => $delay->begin);
       },
 
       # Delayed rendering
       sub {
-        my ($delay, $mojo, $mango) = @_;
+        my ($delay, $mojo, $minion) = @_;
         $c->render(json => {
-          mojo  => $mojo->res->json('/hits/hits/0/_source/release'),
-          mango => $mango->res->json('/hits/hits/0/_source/release')
+          mojo   => $mojo->res->json('/hits/hits/0/_source/release'),
+          minion => $minion->res->json('/hits/hits/0/_source/release')
         });
       }
     );
@@ -500,9 +515,19 @@ created at startup time.
 
   app->start;
 
-Since timers and other low-level event watchers are also independent from
-applications, errors can't get logged automatically, you can change that by
-subscribing to the event L<Mojo::Reactor/"error">.
+Just remember that all events are processed cooperatively, so your callbacks
+shouldn't block for too long.
+
+=head2 Exceptions in events
+
+Since timers and other non-blocking operations are running solely in the event
+loop, outside of the application, exceptions that get thrown in callbacks
+can't get caught and handled automatically. But you can handle them manually
+by subscribing to the event L<Mojo::Reactor/"error"> or catching them inside
+the callback.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
 
   # Forward error messages to the application log
   Mojo::IOLoop->singleton->reactor->on(error => sub {
@@ -510,8 +535,33 @@ subscribing to the event L<Mojo::Reactor/"error">.
     app->log->error($err);
   });
 
-Just remember that all events are processed cooperatively, so your callbacks
-shouldn't block for too long.
+  # Exception only gets logged (and connection times out)
+  get '/connection_times_out' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      die 'This request will not be getting a response';
+    });
+  };
+
+  # Exception gets caught and handled
+  get '/catch_exception' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      eval { die 'This request will be getting a response' };
+      $c->reply->exception($@) if $@;
+    });
+  };
+
+  app->start;
+
+A default subscriber that turns all errors into warnings will usually be added
+by L<Mojo::IOLoop> as a fallback.
+
+  Mojo::IOLoop->singleton->reactor->unsubscribe('error');
+
+During development or for applications where crashing is simply preferable,
+you can also make every exception that gets thrown in a callback fatal by
+removing all of its subscribers.
 
 =head2 WebSocket web service
 
@@ -566,9 +616,7 @@ L<Mojolicious::Controller/"send">.
         };
 
         // Outgoing messages
-        window.setInterval(function() {
-          ws.send('Hello Mojo!');
-        }, 1000);
+        window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
       </script>
     </body>
   </html>
@@ -899,7 +947,7 @@ content repeatedly for multiple requests.
 
   # Send multiple files streaming via PUT and POST
   $ua->put('http://example.com/upload'  => stream => '/home/sri/mojo.png');
-  $ua->post('http://example.com/upload' => stream => '/home/sri/mango.png');
+  $ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
 
 The C<json> and C<form> content generators are always available.
 
@@ -933,7 +981,7 @@ file with L<Mojo::Asset::File/"move_to">.
 
   # Lets fetch the latest Mojolicious tarball
   my $ua = Mojo::UserAgent->new(max_redirects => 5);
-  my $tx = $ua->get('latest.mojolicio.us');
+  my $tx = $ua->get('https://www.github.com/kraih/mojo/tarball/master');
   $tx->res->content->asset->move_to('mojo.tar.gz');
 
 To protect you from excessively large files there is also a limit of 10MB by
@@ -1029,11 +1077,11 @@ can keep many concurrent connections active at the same time.
   my $ua = Mojo::UserAgent->new;
   $ua->get('http://metacpan.org/search?q=mojo' => sub {
     my ($ua, $mojo) = @_;
-    ...
+    say $mojo->res->dom->at('title')->text;
   });
-  $ua->get('http://metacpan.org/search?q=mango' => sub {
-    my ($ua, $mango) = @_;
-    ...
+  $ua->get('http://metacpan.org/search?q=minion' => sub {
+    my ($ua, $minion) = @_;
+    say $minion->res->dom->at('title')->text;
   });
 
   # Start event loop if necessary
@@ -1052,11 +1100,12 @@ synchronize multiple non-blocking requests.
   # Synchronize non-blocking requests
   my $ua    = Mojo::UserAgent->new;
   my $delay = Mojo::IOLoop->delay(sub {
-    my ($delay, $mojo, $mango) = @_;
-    ...
+    my ($delay, $mojo, $minion) = @_;
+    say $mojo->res->dom->at('title')->text;
+    say $minion->res->dom->at('title')->text;
   });
-  $ua->get('http://metacpan.org/search?q=mojo'  => $delay->begin);
-  $ua->get('http://metacpan.org/search?q=mango' => $delay->begin);
+  $ua->get('http://metacpan.org/search?q=mojo'   => $delay->begin);
+  $ua->get('http://metacpan.org/search?q=minion' => $delay->begin);
   $delay->wait;
 
 The call to L<Mojo::IOLoop::Delay/"wait"> makes this code portable, it can now
@@ -1149,7 +1198,7 @@ This can be an invaluable tool for testing your applications.
 For quick hacks and especially testing, L<ojo> one-liners are also a great
 choice.
 
-  $ perl -Mojo -E 'say g("mojolicio.us")->dom->html->head->title->text'
+  $ perl -Mojo -E 'say g("mojolicio.us")->dom->at("title")->text'
 
 =head1 APPLICATIONS
 
@@ -1178,7 +1227,7 @@ C<Authorization> header.
 
 This can be combined with TLS for a secure authentication mechanism.
 
-  $ ./myapp.pl daemon -l https://*:3000?cert=./server.crt&key=./server.key
+  $ ./myapp.pl daemon -l 'https://*:3000?cert=./server.crt&key=./server.key'
 
 =head2 Adding a configuration file
 
@@ -1218,6 +1267,52 @@ 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>. Nested helpers with a prefix based on the plugin name
+are an easy way to avoid conflicts.
+
+  package MyApp::Plugin::MyHelpers;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+    $app->helper('my_helpers.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->my_helpers->render_with_header(text => 'I ♥ Mojolicious!');
+  };
+
+  app->start;
+
+Of course these plugins can contain more than just helpers, take a look at
+L<Mojolicious::Plugins/"PLUGINS"> for a few ideas.
+
 =head2 Adding commands to Mojolicious
 
 By now you've probably used many of the built-in commands described in
@@ -1235,6 +1330,9 @@ that they will be picked up automatically by the command line interface?
 
     # Leak secret passphrases
     say for @{$self->app->secrets} if $args[0] eq 'secrets';
+
+    # Leak mode
+    say $self->app->mode if $args[0] eq 'mode';
   }
 
   1;
@@ -1265,6 +1363,14 @@ L<Mojolicious::Commands/"namespaces">.
 
   1;
 
+Some options like C<-m> for the operating mode of your application are shared
+by all commands automatically.
+
+  $ ./myapp.pl spy -m production mode
+  production
+
+For a full list of shared options see L<Mojolicious::Commands/"SYNOPSIS">.
+
 =head2 Running code against your application
 
 Ever thought about running a quick one-liner against your L<Mojolicious>
@@ -1324,6 +1430,24 @@ get automatically installed with the modules.
 
   1;
 
+Finally a few small changes should be made to the application script. The
+shebang becomes the recommended C<#!perl>, which the toolchain rewrites to the
+proper shebang during installation. Also use L<FindBin> rather than L<lib>,
+since installable scripts can't use L<lib> without breaking updated dual-life
+modules.
+
+  #!perl
+
+  use strict;
+  use warnings;
+
+  use FindBin;
+  BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
+
+  # Start command line interface for application
+  require Mojolicious::Commands;
+  Mojolicious::Commands->start_app('MyApp');
+
 That's really everything, now you can package your application like any other
 CPAN module.
 
@@ -16,7 +16,7 @@ We hope these answers are to your satisfaction.
 
 =head2 How does Mojolicious compare to other Perl web frameworks?
 
-The short answer is "it doesn't", because we interpret the words
+The short answer is "it doesn't", because we interpret the term
 "web framework" much more literally than others. With the emergence of the
 real-time web and new technologies such as WebSockets, we are facing new
 challenges that go way beyond what commonly used modules like L<LWP> were
@@ -24,8 +24,8 @@ designed for. Because of this, L<Mojolicious> contains a whole new HTTP
 client/server stack called L<Mojo>, which was heavily inspired by the original
 LWPng effort and carefully designed with these new requirements in mind. So
 while some of the higher abstraction layers might look similar to other web
-frameworks, it actually defines a whole new category and could even be the
-foundation for more advanced ones in the future.
+frameworks, it is more of a web toolkit and can even be used as the foundation
+for more advanced web frameworks.
 
 =head2 Why doesn't Mojolicious have any dependencies?
 
@@ -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::Socks>, L<IO::Socket::SSL>,
+L<Net::DNS::Native> and L<Plack> to provide advanced functionality if they are
+installed.
 
 =head2 Why reinvent wheels?
 
@@ -172,11 +173,11 @@ one.
 =head2 What does "Your secret passphrase needs to be changed" mean?
 
 L<Mojolicious> uses secret passphrases for security features such as signed
-cookies. It defaults to using the moniker of your application, which is not
-very secure, so we added this log message as a reminder. You can change the
+cookies. It defaults to using L<Mojolicious/"moniker">, which is not very
+secure, so we added this log message as a reminder. You can change the
 passphrase with the attribute L<Mojolicious/"secrets">.
 
-  app->secrets(['My very secret passphrase.']);
+  $app->secrets(['My very secret passphrase.']);
 
 =head2 What does "Nothing has been rendered, expecting delayed response" mean?
 
@@ -8,7 +8,7 @@ Mojolicious::Guides::Growing - Growing
 =head1 OVERVIEW
 
 This document explains the process of starting a L<Mojolicious::Lite>
-prototype from scratch and growing it into a well structured L<Mojolicious>
+prototype from scratch and growing it into a well-structured L<Mojolicious>
 application.
 
 =head1 CONCEPTS
@@ -90,13 +90,13 @@ about previous requests, which makes user-friendly login systems very tricky.
 Sessions solve this problem by allowing web applications to keep stateful
 information across several HTTP requests.
 
-  GET /login?user=sri&pass=s3cret HTTP/1.1
+  GET /login?user=sebastian&pass=s3cret HTTP/1.1
   Host: mojolicio.us
 
   HTTP/1.1 200 OK
   Set-Cookie: sessionid=987654321
   Content-Length: 10
-  Hello sri.
+  Hello sebastian.
 
   GET /protected HTTP/1.1
   Host: mojolicio.us
@@ -105,7 +105,7 @@ information across several HTTP requests.
   HTTP/1.1 200 OK
   Set-Cookie: sessionid=987654321
   Content-Length: 16
-  Hello again sri.
+  Hello again sebastian.
 
 Traditionally all session data was stored on the server-side and only session
 ids were exchanged between browser and web server in the form of cookies.
@@ -145,11 +145,12 @@ organized CPAN distribution to maximize maintainability.
 
   myapp                      # Application directory
   |- script                  # Script directory
-  |  +- myapp                # Application script
+  |  +- my_app               # Application script
   |- 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
@@ -169,6 +170,9 @@ L<Mojolicious::Command::generate::app>.
   $ mojo generate lite_app myapp.pl
   $ mojo generate app MyApp
 
+Feature-wise both are almost equal, the only real differences are
+organizational, so each one can be gradually transformed into the other.
+
 =head2 Foundation
 
 We start our new application with a single executable Perl script.
@@ -193,34 +197,62 @@ This will be the foundation for our login manager example application.
 The built-in development web server makes working on your application a lot of
 fun thanks to automatic reloading.
 
-  $ morbo myapp.pl
+  $ morbo ./myapp.pl
   Server available at http://127.0.0.1:3000.
 
 Just save your changes and they will be automatically in effect the next time
 you refresh your browser.
 
+=head2 A birds-eye view
+
+It all starts with an HTTP request like this, sent by your browser.
+
+  GET / HTTP/1.1
+  Host: localhost:3000
+
+Once the request has been received by the web server through the event loop,
+it will be passed on to L<Mojolicious>, where it will be handled in a few
+simple steps.
+
+  1. Check if a static file exists that would meet the requirements.
+  2. Try to find a route that would meet the requirements.
+  3. Dispatch the request to this route, usually reaching one or more actions.
+  4. Process the request, maybe generating a response with the renderer.
+  5. Return control to the web server, and if no response has been generated
+     yet, wait for a non-blocking operation to do so through the event loop.
+
+With our application the router would have found an action in step 2, and
+rendered some text in step 4, resulting in an HTTP response like this being
+sent back to the browser.
+
+  HTTP/1.1 200 OK
+  Content-Length: 12
+  Hello world!
+
 =head2 Model
 
 In L<Mojolicious> we consider web applications simple frontends for existing
-business logic, that means L<Mojolicious> is by design entirely L<model> layer
+business logic, that means L<Mojolicious> is by design entirely I<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;
 
   my $USERS = {
-    sri    => 'secr3t',
-    marcus => 'lulz',
-    yko    => 'zeecaptain'
+    joel      => 'las3rs',
+    marcus    => 'lulz',
+    sebastian => 'secr3t'
   };
 
   sub new { bless {}, shift }
@@ -245,12 +277,12 @@ 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
+  # /?user=sebastian&pass=secr3t
   any '/' => sub {
     my $c = shift;
 
@@ -304,8 +336,8 @@ on L<Mojo::DOM>.
     ->element_exists('form input[type="submit"]');
 
   # Test login with valid credentials
-  $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'})
-    ->status_is(200)->text_like('html body' => qr/Welcome sri/);
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)->text_like('html body' => qr/Welcome sebastian/);
 
   # Test accessing a protected page
   $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
@@ -331,8 +363,8 @@ L<Mojolicious::Command::get>.
   $ ./myapp.pl get /
   Wrong username or password.
 
-  $ ./myapp.pl get -v '/?user=sri&pass=secr3t'
-  GET /?user=sri&pass=secr3t HTTP/1.1
+  $ ./myapp.pl get -v '/?user=sebastian&pass=secr3t'
+  GET /?user=sebastian&pass=secr3t HTTP/1.1
   User-Agent: Mojolicious (Perl)
   Connection: keep-alive
   Accept-Encoding: gzip
@@ -346,7 +378,7 @@ L<Mojolicious::Command::get>.
   Content-Length: 12
   Content-Type: text/plain
 
-  Welcome sri.
+  Welcome sebastian.
 
 =head2 State keeping
 
@@ -355,12 +387,12 @@ using the method L<Mojolicious::Controller/"session">, there is no setup
 required, but we suggest setting a more secure passphrase with
 L<Mojolicious/"secrets">.
 
-  app->secrets(['Mojolicious rocks']);
+  $app->secrets(['Mojolicious rocks']);
 
 This passphrase is used by the HMAC-SHA1 algorithm to make signed cookies
 secure and can be changed at any time to invalidate all existing sessions.
 
-  $c->session(user => 'sri');
+  $c->session(user => 'sebastian');
   my $user = $c->session('user');
 
 By default all sessions expire after one hour, for more control you can use
@@ -374,8 +406,9 @@ set an absolute expiration date in the past.
   $c->session(expires => 1);
 
 For data that should only be visible on the next request, like a confirmation
-message after a 302 redirect, you can use the flash, accessible through the
-method L<Mojolicious::Controller/"flash">.
+message after a C<302> redirect performed with
+L<Mojolicious::Controller/"redirect_to">, you can use the flash, accessible
+through the method L<Mojolicious::Controller/"flash">.
 
   $c->flash(message => 'Everything is fine.');
   $c->redirect_to('goodbye');
@@ -393,12 +426,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 {
@@ -479,10 +512,21 @@ this.
     <body><%= content %></body>
   </html>
 
-A list of all built-in helpers can be found in
-L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+And the directory structure should be looking like this now.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  +- t
+     +- login.t
 
-=head1 WELL STRUCTURED APPLICATION
+Our templates are using quite a few features of the renderer,
+L<Mojolicious::Guides::Rendering> explains them all in great detail.
+
+=head1 WELL-STRUCTURED APPLICATION
 
 Due to the flexibility of L<Mojolicious> there are many variations of the
 actual growing process, but this should give you a good overview of the
@@ -496,8 +540,8 @@ directories with the command L<Mojolicious::Command::inflate>.
 
   $ ./myapp.pl inflate
 
-Those directories always get priority, so inflating can also be a great way to
-allow your users to customize their applications.
+Those directories have a higher precedence, so inflating can also be a great
+way to allow your users to customize their applications.
 
 =head2 Simplified application class
 
@@ -514,13 +558,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;
 
@@ -568,24 +612,42 @@ allow running tests again.
   use warnings;
 
   use lib 'lib';
-  use Mojolicious::Commands;
 
   # Start command line interface for application
+  require Mojolicious::Commands;
   Mojolicious::Commands->start_app('MyApp');
 
+And the directory structure of our hybrid application should be looking like
+this.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     |- index.html.ep
+     +- protected.html.ep
+
 =head2 Controller class
 
 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,30 +688,32 @@ 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');
   }
 
   1;
 
-L<Mojolicious::Routes> allows many route variations, choose whatever you like
-most.
+The router allows many different route variations,
+L<Mojolicious::Guides::Routing> explains them all in great detail.
 
 =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
@@ -657,32 +721,16 @@ appropriate directories.
 
 =head2 Script
 
-Finally C<myapp.pl> can be replaced with a proper L<Mojolicious> script.
+Finally C<myapp.pl> can be moved into a C<script> directory and renamed to
+C<my_app> to follow the CPAN standard.
 
-  $ rm myapp.pl
   $ mkdir script
-  $ touch script/myapp
-  $ chmod 744 script/myapp
-
-Only a few small details change, since installable scripts can't use L<lib>
-without breaking updated dual-life modules.
-
-  #!/usr/bin/env perl
-
-  use strict;
-  use warnings;
-
-  use FindBin;
-  BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
-  # Start command line interface for application
-  require Mojolicious::Commands;
-  Mojolicious::Commands->start_app('MyApp');
+  $ mv myapp.pl script/my_app
 
 =head2 Simplified tests
 
-Normal L<Mojolicious> applications are a little easier to test, so
-C<t/login.t> can be simplified.
+Full L<Mojolicious> applications are a little easier to test, so C<t/login.t>
+can be simplified.
 
   use Test::More;
   use Test::Mojo;
@@ -697,8 +745,8 @@ C<t/login.t> can be simplified.
     ->element_exists('form input[name="pass"]')
     ->element_exists('form input[type="submit"]');
 
-  $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'})
-    ->status_is(200)->text_like('html body' => qr/Welcome sri/);
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)->text_like('html body' => qr/Welcome sebastian/);
 
   $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
 
@@ -709,6 +757,27 @@ C<t/login.t> can be simplified.
 
   done_testing();
 
+And our final directory structure should be looking like this.
+
+  myapp
+  |- script
+  |  +- my_app
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     |- Controller
+  |     |  +- Login.pm
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     +- login
+        |- index.html.ep
+        +- protected.html.ep
+
 Test-driven development takes a little getting used to, but can be a very
 powerful tool.
 
@@ -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
@@ -137,14 +144,19 @@ backslash.
   in multiple\\
   lines
 
-Stash values that don't have invalid characters in their name get
-automatically initialized as normal variables in the template, and the
-controller object as both C<$self> and C<$c>.
+At the beginning of the template, stash values that don't have invalid
+characters in their name get automatically initialized as normal variables,
+and the controller object as both C<$self> and C<$c>.
 
   $c->stash(name => 'tester');
 
   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'} %>
@@ -243,7 +255,8 @@ too.
 =head2 Rendering text
 
 Characters can be rendered to bytes with the C<text> stash value, the given
-content will be automatically encoded to bytes.
+content will be automatically encoded with
+L<Mojolicious::Renderer/"encoding">.
 
   $c->render(text => 'I ♥ Mojolicious!');
 
@@ -295,16 +308,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 +325,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
@@ -392,8 +446,9 @@ By now you've probably already encountered the built-in 404 (Not Found) and
 500 (Server Error) pages, that get rendered automatically when you make a
 mistake. Those are fallbacks for when your own exception handling fails, but
 especially during development they can also be a great help, you can render
-them manually with the methods L<Mojolicious::Controller/"render_exception">
-and L<Mojolicious::Controller/"render_not_found">.
+them manually with the helpers
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception"> and
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
 
   use Mojolicious::Lite;
   use Scalar::Util 'looks_like_number';
@@ -403,11 +458,11 @@ and L<Mojolicious::Controller/"render_not_found">.
     my ($dividend, $divisor) = $c->param(['dividend', 'divisor']);
 
     # 404
-    return $c->render_not_found
+    return $c->reply->not_found
       unless looks_like_number $dividend && looks_like_number $divisor;
 
     # 500
-    return $c->render_exception('Division by zero!') if $divisor == 0;
+    return $c->reply->exception('Division by zero!') if $divisor == 0;
 
     # 200
     $c->render(text => $dividend / $divisor);
@@ -455,29 +510,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
@@ -540,7 +572,7 @@ value).
 
   my $html = $c->render_to_string('reminder', layout => 'mail');
 
-=head2 Including partial templates
+=head2 Including additional templates
 
 Like most helpers L<Mojolicious::Plugin::DefaultHelpers/"include"> is just a
 shortcut to make your life a little easier.
@@ -548,25 +580,26 @@ shortcut to make your life a little easier.
   @@ foo/bar.html.ep
   <!DOCTYPE html>
   <html>
-    %= include 'header'
+    %= include 'header', title => 'Howdy'
     <body>Bar</body>
   </html>
 
   @@ header.html.ep
-  <head><title>Howdy</title></head>
+  <head><title><%= $title %></title></head>
 
 Instead of C<include> you could also just call
-L<Mojolicious::Controller/"render_to_string">.
+L<Mojolicious::Controller/"render_to_string"> to include one template into
+another.
 
   @@ foo/bar.html.ep
   <!DOCTYPE html>
   <html>
-    %= $c->render_to_string('header')
+    %= $c->render_to_string('header', title => 'Howdy')
     <body>Bar</body>
   </html>
 
   @@ header.html.ep
-  <head><title>Howdy</title></head>
+  <head><title><%= $title %></title></head>
 
 =head2 Reusable template blocks
 
@@ -609,6 +642,95 @@ A naive translation to Perl code could look like this.
   }
   return $output;
 
+While template blocks cannot be shared between templates, they are most
+commonly used to pass parts of a template to helpers.
+
+=head2 Adding helpers
+
+You should always try to keep your actions small and reuse as much code as
+possible. Helpers make this very easy, you can use them to do pretty much
+anything an action could do.
+
+  use Mojolicious::Lite;
+
+  helper debug => sub {
+    my ($c, $str) = @_;
+    $c->app->log->debug($str);
+  };
+
+  get '/' => sub {
+    my $c = shift;
+    $c->debug('Hello from an action!');
+  } => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  % 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. Wrapping the helper
+result into a L<Mojo::ByteStream> object can prevent accidental double
+escaping.
+
+  use Mojolicious::Lite;
+  use Mojo::ByteStream;
+
+  helper trim_newline => sub {
+    my ($c, $block) = @_;
+    my $result = $block->();
+    $result =~ s/\n//g;
+    return Mojo::ByteStream->new($result);
+  };
+
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  %= trim_newline begin
+    Some text.
+    %= 1 + 1
+    More text.
+  % end
+
+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;
+
+While helpers can also be redefined, this should only be done very carefully
+to avoid conflicts.
+
 =head2 Content blocks
 
 Blocks and the helper L<Mojolicious::Plugin::DefaultHelpers/"content_for">
@@ -669,12 +791,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;
 
@@ -796,6 +918,10 @@ L<Mojolicious::Validator::Validation/"error">.
     </body>
   </html>
 
+The methods L<Mojolicious::Controller/"flash"> and
+L<Mojolicious::Controller/"redirect_to"> are often used together to prevent
+double form submission.
+
 =head2 Cross-site request forgery
 
 CSRF is a very common attack on web applications that trick your logged in
@@ -839,56 +965,59 @@ with L<Mojolicious::Validator::Validation/"csrf_protect">.
 
 The token can also be submitted with the C<X-CSRF-Token> request header.
 
-=head2 Adding helpers
+=head1 ADVANCED
 
-Adding and redefining helpers is very easy, you can use them to do pretty much
-everything.
+Less commonly used and more powerful features.
 
-  use Mojolicious::Lite;
+=head2 Serving static files
 
-  helper debug => sub {
-    my ($c, $str) = @_;
-    $c->app->log->debug($str);
-  };
+Static files are automatically served from your C<DATA> sections and C<public>
+directories, and if that's not enough you can also serve them manually with
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>static">.
 
-  get '/' => sub {
+  use Mojolicious::Lite;
+
+  get '/some_static_file' => sub {
     my $c = shift;
-    $c->debug('Hello from an action!');
-  } => 'index';
+    $c->res->headers->content_disposition('attachment; filename=bar.png;');
+    $c->reply->static('foo/bar.png');
+  };
 
   app->start;
-  __DATA__
 
-  @@ index.html.ep
-  % debug 'Hello from a template!';
+=head2 Custom responses
 
-Helpers can also accept template blocks as last argument, this for example
-allows very pleasant to use tag helpers and filters.
+Most response content, static as well as dynamic, gets served through
+L<Mojo::Asset::File> and L<Mojo::Asset::Memory> objects. For somewhat static
+content, like cached JSON data or temporary file, you can create your own and
+use the helper L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>asset"> to
+serve them while allowing content negotiation to be performed with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> headers.
 
   use Mojolicious::Lite;
-  use Mojo::ByteStream;
 
-  helper trim_newline => sub {
-    my ($c, $block) = @_;
-    my $result = $block->();
-    $result =~ s/\n//g;
-    return Mojo::ByteStream->new($result);
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
   };
 
-  get '/' => 'index';
-
   app->start;
-  __DATA__
 
-  @@ index.html.ep
-  %= trim_newline begin
-    Some text.
-    %= 1 + 1
-    More text.
-  % end
+For even more control you can also just skip the helper and use
+L<Mojolicious::Controller/"rendered"> to tell the renderer when you're done
+generating a response.
 
-Wrapping the helper result into a L<Mojo::ByteStream> object can prevent
-accidental double escaping.
+  use Mojolicious::Lite;
+
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
+    $c->rendered(200);
+  };
+
+  app->start;
 
 =head2 Helper plugins
 
@@ -1016,29 +1145,6 @@ plugin.
   @@ alertassets.html.ep
   %= javascript "/alertassets.js"
 
-=head1 ADVANCED
-
-Less commonly used and more powerful features.
-
-=head2 Rendering static files
-
-If automatic rendering of static files is not enough, you can also render them
-manually from your C<DATA> sections and C<public> directories with
-L<Mojolicious::Controller/"render_static">.
-
-  $c->res->headers->content_disposition('attachment; filename=bar.png;');
-  $c->render_static('foo/bar.png');
-
-=head2 Custom responses
-
-For entirely custom responses to, for example, stream content directly from
-files, you can use L<Mojolicious::Controller/"rendered"> to tell the renderer
-that a response has been generated.
-
-  $c->res->headers->content_type('text/plain');
-  $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
-  $c->rendered(200);
-
 =head2 Post-processing dynamic content
 
 While post-processing tasks are generally very easy with the hook
@@ -1084,15 +1190,28 @@ L<Mojolicious::Controller/"write_chunk"> come in handy. A common use would be
 to send the C<head> section of an HTML document to the browser in advance and
 speed up preloading of referenced images and stylesheets.
 
-  $c->write_chunk('<html><head><title>Example</title></head>' => sub {
+  use Mojolicious::Lite;
+
+  get '/' => sub {
     my $c = shift;
-    $c->finish('<body>Example</body></html>');
-  });
+    $c->write_chunk('<html><head><title>Example</title></head>' => sub {
+      my $c = shift;
+      $c->finish('<body>Example</body></html>');
+    });
+  };
+
+  app->start;
 
 The optional drain callback ensures that all previous chunks have been
 written before processing continues. An empty chunk or call to
 L<Mojolicious::Controller/"finish"> marks the end of the stream.
 
+  HTTP/1.1 200 OK
+  Connection: keep-alive
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Transfer-Encoding: chunked
+  Server: Mojolicious (Perl)
+
   29
   <html><head><title>Example</title></head>
   1b
@@ -1241,22 +1360,22 @@ that need to be automatically encoded, but this can be easily disabled if
 you're generating bytes instead.
 
   use Mojolicious::Lite;
-  use Mango::BSON ':bson';
+  use Storable 'nfreeze';
 
-  # Add "bson" handler
-  app->renderer->add_handler(bson => sub {
+  # Add "storable" handler
+  app->renderer->add_handler(storable => sub {
     my ($renderer, $c, $output, $options) = @_;
 
     # Disable automatic encoding
     delete $options->{encoding};
 
-    # Encode BSON data from stash value
-    $$output = bson_encode delete $c->stash->{bson};
+    # Encode data from stash value
+    $$output = nfreeze delete $c->stash->{storable};
 
     return 1;
   });
 
-  get '/' => {bson => {i => '♥ mojolicious'}, handler => 'bson'};
+  get '/' => {storable => {i => '♥ mojolicious'}, handler => 'storable'};
 
   app->start;
 
@@ -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
@@ -72,7 +72,7 @@ will learn more about this later on. Internally routes get compiled to regular
 expressions, so you can get the best of both worlds with a little bit of
 experience.
 
-  /user/show/:id -> qr/(?-xism:^\/user\/show/([^\/\.]+))/
+  /user/show/:id -> qr/(?-xism:^\/user\/show/([^\/.]+))/
 
 A trailing slash in the path is always optional.
 
@@ -164,16 +164,16 @@ defined.
     my $r = $self->routes;
 
     # Route
-    $r->route('/welcome')->to(controller => 'foo', action => 'welcome');
+    $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
   }
 
   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
@@ -191,17 +191,54 @@ class, but the router can be accessed from everywhere (even at runtime).
 
 =head2 Routing destination
 
-After you start a new route with the method
-L<Mojolicious::Routes::Route/"route">, you can also give it a destination in
-the form of a hash using the chained method
-L<Mojolicious::Routes::Route/"to">.
+After you start a new route with methods like
+L<Mojolicious::Routes::Route/"get">, you can also give it a destination in the
+form of a hash using the chained method L<Mojolicious::Routes::Route/"to">.
 
   # /welcome -> {controller => 'foo', action => 'welcome'}
-  $r->route('/welcome')->to(controller => 'foo', action => 'welcome');
+  $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
 
 Now if the route matches an incoming request it will use the content of this
 hash to try and find appropriate code to generate a response.
 
+=head2 HTTP methods
+
+There are already shortcuts for the most common HTTP request methods like
+L<Mojolicious::Routes::Route/"post">, and for more control
+L<Mojolicious::Routes::Route/"any"> accepts an optional array reference with
+arbitrary request methods as first argument.
+
+  # PUT /hello  -> undef
+  # GET /hello  -> {controller => 'foo', action => 'hello'}
+  $r->get('/hello')->to(controller => 'foo', action => 'hello');
+
+  # PUT /hello -> {controller => 'foo', action => 'hello'}
+  $r->put('/hello')->to(controller => 'foo', action => 'hello');
+
+  # POST /hello -> {controller => 'foo', action => 'hello'}
+  $r->post('/hello')->to(controller => 'foo', action => 'hello');
+
+  # GET|POST /bye  -> {controller => 'foo', action => 'bye'}
+  $r->any([qw(GET POST)] => '/bye')->to(controller => 'foo', action => 'bye');
+
+  # * /whatever -> {controller => 'foo', action => 'whatever'}
+  $r->any('/whatever')->to(controller => 'foo', action => 'whatever');
+
+There is one small exception, C<HEAD> requests are considered equal to C<GET>,
+but content will not be sent with the response even if it is present.
+
+  # GET /test  -> {controller => 'bar', action => 'test'}
+  # HEAD /test -> {controller => 'bar', action => 'test'}
+  $r->get('/test')->to(controller => 'bar', action => 'test');
+
+=head2 IRIs
+
+IRIs are handled transparently, that means paths are guaranteed to be
+unescaped and decoded from bytes to characters.
+
+  # GET /☃ (Unicode snowman) -> {controller => 'foo', action => 'snowman'}
+  $r->get('/☃')->to(controller => 'foo', action => 'snowman');
+
 =head2 Stash
 
 The generated hash of a matching route is actually the center of the whole
@@ -209,7 +246,7 @@ L<Mojolicious> request cycle. We call it the stash, and it persists until a
 response has been generated.
 
   # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
-  $r->route('/bye')
+  $r->get('/bye')
     ->to(controller => 'foo', action => 'bye', mymessage => 'Bye');
 
 There are a few stash values with special meaning, such as C<controller> and
@@ -238,30 +275,29 @@ endpoints of these nested routes can.
 
   # /foo     -> undef
   # /foo/bar -> {controller => 'foo', action => 'bar'}
-  my $foo = $r->route('/foo')->to(controller => 'foo');
-  $foo->route('/bar')->to(action => 'bar');
+  my $foo = $r->any('/foo')->to(controller => 'foo');
+  $foo->get('/bar')->to(action => 'bar');
 
 The stash is simply inherited from route to route and newer values override
 old ones.
 
-  # /foo     -> undef
-  # /foo/abc -> undef
-  # /foo/bar -> {controller => 'foo', action => 'bar'}
-  # /foo/baz -> {controller => 'foo', action => 'baz'}
-  # /foo/cde -> {controller => 'foo', action => 'abc'}
-  my $foo = $r->route('/foo')->to(controller => 'foo', action => 'abc');
-  $foo->route('/bar')->to(action => 'bar');
-  $foo->route('/baz')->to(action => 'baz');
-  $foo->route('/cde');
+  # /cats      -> {controller => 'cats', action => 'index'}
+  # /cats/nyan -> {controller => 'cats', action => 'nyan'}
+  # /cats/lol  -> {controller => 'cats', action => 'default'}
+  my $cats = $r->any('/cats')->to(controller => 'cats', action => 'default');
+  $cats->get('/')->to(action => 'index');
+  $cats->get('/nyan')->to(action => 'nyan');
+  $cats->get('/lol');
 
 =head2 Special stash values
 
 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 +306,14 @@ sensitive.
   sub startup {
     my $self = shift;
 
-    # /bye -> {controller => 'foo', action => 'bye'} -> MyApp::Foo->bye
-    $self->routes->route('/bye')->to(controller => 'foo', action => 'bye');
+    # /bye -> MyApp::Controller::Foo->bye
+    $self->routes->get('/bye')->to(controller => 'foo', action => 'bye');
   }
 
   1;
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -296,52 +332,107 @@ used ones they also got a special shortcut in the form of
 C<controller#action>.
 
   # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
-  $r->route('/bye')->to('foo#bye', mymessage => 'Bye');
+  $r->get('/bye')->to('foo#bye', mymessage => 'Bye');
+
+During camelization C<-> characters get replaced with C<::>, this allows
+multi-level C<controller> hierarchies.
 
-During camelization C<-> gets replaced with C<::>, this allows multi-level
-C<controller> hierarchies.
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->get('/')->to('foo-bar#hi');
 
-  # / -> {controller => 'foo-bar', action => 'hi'} -> MyApp::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->get('/')->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.
 
-=head2 Route to class
+=head2 Namespaces
 
 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
-  $r->route('/bye')
-    ->to(namespace => 'MyApp::Controller::Foo::Bar', action => 'bye');
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->get('/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->get('/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->get('/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
 
 The C<cb> stash value, which won't be inherited by nested routes, can be used
 to bypass controllers and execute a callback instead.
 
-  $r->route('/bye')->to(cb => sub {
+  $r->get('/bye')->to(cb => sub {
     my $c = shift;
     $c->render(text => 'Good bye.');
   });
 
-This technique is the foundation of L<Mojolicious::Lite>, you can learn more
-about it from the included tutorial.
+But just like in L<Mojolicious::Lite> you can also pass the callback directly,
+which usually looks much better.
+
+  $r->get('/bye' => sub {
+    my $c = shift;
+    $c->render(text => 'Good bye.');
+  });
+
+=head2 Named routes
+
+Naming your routes will allow backreferencing in many methods and helpers
+throughout the whole framework, most of them internally rely on
+L<Mojolicious::Controller/"url_for"> for this.
+
+  # /foo/marcus -> {controller => 'foo', action => 'bar', user => 'marcus'}
+  $r->get('/foo/:user')->to('foo#bar')->name('baz');
+
+  # Generate URL "/foo/marcus" for route "baz"
+  my $url = $c->url_for('baz');
+
+  # Generate URL "/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan');
+
+  # Generate URL "http://127.0.0.1:3000/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan')->to_abs;
+
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters, custom names have a
+higher precedence though.
+
+  # /foo/bar ("foobar")
+  $r->get('/foo/bar')->to('test#stuff');
+
+  # Generate URL "/foo/bar"
+  my $url = $c->url_for('foobar');
+
+To refer to the current route you can use the reserved name C<current> or no
+name at all.
+
+  # Generate URL for current route
+  my $url = $c->url_for('current');
+  my $url = $c->url_for;
+
+To check or get the name of the current route you can use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
+
+  # Name for current route
+  my $name = $c->current_route;
+
+  # Check route name in code shared by multiple routes
+  $c->stash(button => 'green') if $c->current_route('login');
 
 =head2 Optional placeholders
 
@@ -350,21 +441,18 @@ already exist.
 
   # /bye -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
   # /hey -> {controller => 'foo', action => 'bar', mymessage => 'hey'}
-  $r->route('/:mymessage')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
 
 One more interesting effect, a placeholder automatically becomes optional if
 there is already a stash value of the same name present, this works similar to
 the regular expression C<([^/.]+)?>.
 
   # / -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
-  $r->route('/:mymessage')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
 
   # /test/123     -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
   # /test/bye/123 -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
-  $r->route('/test/:mymessage/123')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/test/:mymessage/123')->to('foo#bar', mymessage => 'hi');
 
 This is also the case if multiple placeholders are right after another and not
 separated by other characters than C</>.
@@ -372,8 +460,7 @@ separated by other characters than C</>.
   # /           -> {controller => 'foo',   action => 'bar'}
   # /users      -> {controller => 'users', action => 'bar'}
   # /users/list -> {controller => 'users', action => 'list'}
-  $r->route('/:controller/:action')
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:controller/:action')->to('foo#bar');
 
 Special stash values like C<controller> and C<action> can also be
 placeholders, which is very convenient especially during development, but
@@ -394,11 +481,10 @@ A very easy way to make placeholders more restrictive are alternatives, you
 just make a list of possible values, which then work similar to the regular
 expression C<(bender|leela)>.
 
+  # /fry    -> undef
   # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
   # /leela  -> {controller => 'foo', action => 'bar', name => 'leela'}
-  # /fry    -> undef
-  $r->route('/:name', name => [qw(bender leela)])
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:name' => [name => [qw(bender leela)]])->to('foo#bar');
 
 You can also adjust the regular expressions behind placeholders directly, just
 make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
@@ -407,17 +493,75 @@ is fine though.
 
   # /23   -> {controller => 'foo', action => 'bar', number => 23}
   # /test -> undef
-  $r->route('/:number', number => qr/\d+/)
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:number' => [number => qr/\d+/])->to('foo#bar');
 
   # /23   -> undef
   # /test -> {controller => 'foo', action => 'bar', name => 'test'}
-  $r->route('/:name', name => qr/[a-zA-Z]+/)
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:name' => [name => qr/[a-zA-Z]+/])->to('foo#bar');
 
 This way you get easily readable routes and the raw power of regular
 expressions.
 
+=head2 Under
+
+To share code with multiple nested routes you can use
+L<Mojolicious::Routes::Route/"under">, because unlike normal nested routes,
+the routes generated with it have their own intermediate destination and
+result in additional dispatch cycles when they match.
+
+  # /foo     -> undef
+  # /foo/bar -> {controller => 'foo', action => 'baz'}
+  #             {controller => 'foo', action => 'bar'}
+  my $foo = $r->under('/foo')->to('foo#baz');
+  $foo->get('/bar')->to('#bar');
+
+The actual action code for this destination needs to return a true value or
+the dispatch chain will be broken, this can be a very powerful tool for
+authentication.
+
+  # /blackjack -> {cb => sub {...}}
+  #               {controller => 'hideout', action => 'blackjack'}
+  my $auth = $r->under('/' => sub {
+    my $c = shift;
+
+    # Authenticated
+    return 1 if $c->req->headers->header('X-Bender');
+
+    # Not authenticated
+    $c->render(text => "You're not Bender.");
+    return undef;
+  });
+  $auth->get('/blackjack')->to('hideout#blackjack');
+
+Broken dispatch chains can be continued by calling the method
+L<Mojolicious::Controller/"continue">, this allows for example non-blocking
+operations to finish before reaching the next dispatch cycle.
+
+  my $maybe = $r->under('/maybe' => sub {
+    my $c = shift;
+
+    # Wait 3 seconds and then give visitors a 50% chance to continue
+    Mojo::IOLoop->timer(3 => sub {
+
+      # Loser
+      return $c->render(text => 'No luck.') unless int rand 2;
+
+      # Winner
+      $c->continue;
+    });
+
+    return undef;
+  });
+  $maybe->get('/')->to('maybe#winner');
+
+Every destination is just a snapshot of the stash at the time the route
+matched, and only the C<format> value is shared by all of them. For a little
+more power you can introspect the preceding and succeeding destinations with
+L<Mojolicious::Controller/"match">.
+
+  # Action of the fourth dispatch cycle
+  my $action = $c->match->stack->[3]{action};
+
 =head2 Formats
 
 File extensions like C<.html> and C<.txt> at the end of a route are
@@ -426,29 +570,23 @@ automatically detected and stored in the stash value C<format>.
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
   # /foo.txt  -> {controller => 'foo', action => 'bar', format => 'txt'}
-  $r->route('/foo')->to(controller => 'foo', action => 'bar');
+  $r->get('/foo')->to('foo#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.txt -> undef
   # /foo.rss -> {controller => 'foo', action => 'bar', format => 'rss'}
   # /foo.xml -> {controller => 'foo', action => 'bar', format => 'xml'}
-  # /foo.txt -> undef
-  $r->route('/foo', format => [qw(rss xml)])
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/foo' => [format => [qw(rss xml)]])->to('foo#bar');
 
 Or you can just disable format detection, which gets inherited by nested
 routes and allows selective re-enabling.
 
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> undef
-  $r->route('/foo', format => 0)->to('foo#bar');
+  $r->get('/foo' => [format => 0])->to('foo#bar');
 
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> undef
@@ -456,77 +594,9 @@ routes and allows selective re-enabling.
   # /baz.txt  -> {controller => 'baz', action => 'yada', format => 'txt'}
   # /baz.html -> {controller => 'baz', action => 'yada', format => 'html'}
   # /baz.xml  -> undef
-  my $inactive = $r->route(format => 0);
-  $inactive->route('/foo')->to('foo#bar');
-  $inactive->route('/baz', format => [qw(txt html)])->to('baz#yada');
-
-=head2 Named routes
-
-Naming your routes will allow backreferencing in many methods and helpers
-throughout the whole framework, most of them internally rely on
-L<Mojolicious::Controller/"url_for"> for this.
-
-  # /foo/abc -> {controller => 'foo', action => 'bar', name => 'abc'}
-  $r->route('/foo/:name')->name('test')
-    ->to(controller => 'foo', action => 'bar');
-
-  # Generate URL "/foo/abc" for route "test"
-  my $url = $c->url_for('test');
-
-  # Generate URL "/foo/sebastian" for route "test"
-  my $url = $c->url_for('test', name => 'sebastian');
-
-  # Generate URL "http://127.0.0.1:3000/foo/sebastian" for route "test"
-  my $url = $c->url_for('test', name => 'sebastian')->to_abs;
-
-Nameless routes get an automatically generated one assigned that is simply
-equal to the route itself without non-word characters.
-
-  # /foo/bar ("foobar")
-  $r->route('/foo/bar')->to('test#stuff');
-
-  # Generate URL "/foo/bar"
-  my $url = $c->url_for('foobar');
-
-To refer to the current route you can use the reserved name C<current> or no
-name at all.
-
-  # Generate URL for current route
-  my $url = $c->url_for('current');
-  my $url = $c->url_for;
-
-To check or get the name of the current route you can use the helper
-L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
-
-  # Name for current route
-  my $name = $c->current_route;
-
-  # Check route name in code shared by multiple routes
-  $c->stash(button => 'green') if $c->current_route('login');
-
-=head2 HTTP methods
-
-The method L<Mojolicious::Routes::Route/"via"> allows only specific HTTP
-methods to pass.
-
-  # GET /bye    -> {controller => 'foo', action => 'bye'}
-  # POST /bye   -> undef
-  # DELETE /bye -> undef
-  $r->route('/bye')->via('GET')->to(controller => 'foo', action => 'bye');
-
-  # GET /bye    -> {controller => 'foo', action => 'bye'}
-  # POST /bye   -> {controller => 'foo', action => 'bye'}
-  # DELETE /bye -> undef
-  $r->route('/bye')->via('GET', 'POST')
-    ->to(controller => 'foo', action => 'bye');
-
-With one small exception, C<HEAD> requests are considered equal to C<GET> and
-content will not be sent with the response.
-
-  # GET /test  -> {controller => 'bar', action => 'test'}
-  # HEAD /test -> {controller => 'bar', action => 'test'}
-  # PUT /test  -> undef
-  $r->route('/test')->via('GET')->to(controller => 'bar', action => 'test');
+  my $inactive = $r->under([format => 0]);
+  $inactive->get('/foo')->to('foo#bar');
+  $inactive->get('/baz' => [format => [qw(txt html)]])->to('baz#yada');
 
 =head2 WebSockets
 
@@ -535,10 +605,10 @@ access to WebSocket handshakes, which are normal C<GET> requests with some
 additional information.
 
   # /echo (WebSocket handshake)
-  $r->websocket('/echo')->to(controller => 'foo', action => 'echo');
+  $r->websocket('/echo')->to('foo#echo');
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -557,110 +627,17 @@ request with a C<101> response status, which happens automatically if you
 subscribe to an event with L<Mojolicious::Controller/"on"> or send a message
 with L<Mojolicious::Controller/"send"> right away.
 
-=head2 Bridges
-
-Bridge routes created with the method L<Mojolicious::Routes::Route/"bridge">
-can be used to share code with multiple nested routes, because unlike normal
-nested routes, they result in additional dispatch cycles when they match.
-
-  # /foo     -> undef
-  # /foo/bar -> {controller => 'foo', action => 'baz'}
-  #             {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(controller => 'foo', action => 'baz');
-  $foo->route('/bar')->to(action => 'bar');
-
-The actual bridge code needs to return a true value or the dispatch chain will
-be broken, this makes bridges a very powerful tool for authentication.
-
-  # /foo     -> undef
-  # /foo/bar -> {cb => sub {...}}
-  #             {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(cb => sub {
-    my $c = shift;
-
-    # Authenticated
-    return 1 if $c->req->headers->header('X-Bender');
-
-    # Not authenticated
-    $c->render(text => "You're not Bender.");
-    return undef;
-  });
-  $foo->route('/bar')->to(controller => 'foo', action => 'bar');
-
-Broken dispatch chains can be continued by calling the method
-L<Mojolicious::Controller/"continue">, this allows for example non-blocking
-operations to finish before reaching the next dispatch cycle.
-
-  # /foo     -> undef
-  # /foo/bar -> {cb => sub {...}}
-  #          -> {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(cb => sub {
-    my $c = shift;
-
-    # Wait 3 seconds and then give visitors a 50% chance to continue
-    Mojo::IOLoop->timer(3 => sub {
-
-      # Loser
-      return $c->render(text => 'No luck.') unless int rand 2;
-
-      # Winner
-      $c->continue;
-    });
-
-    return undef;
-  });
-  $foo->route('/bar')->to(controller => 'foo', action => 'bar');
-
-=head2 More convenient routes
-
-From the tutorial you should already know L<Mojolicious::Lite> routes, which
-are in fact just a small convenience layer around everything described above
-and accessible through methods like L<Mojolicious::Routes::Route/"get"> and
-L<Mojolicious::Routes::Route/"any"> as part of the normal router.
-
-  # POST /foo -> {controller => 'foo', action => 'abc'}
-  $r->post('/foo')->to(controller => 'foo', action => 'abc');
-
-  # PATCH /bar -> {controller => 'foo', action => 'bar', test => 23}
-  $r->patch('/bar')->to('foo#bar', test => 23);
-
-  # GET /baz -> {template => 'foo/bar'}
-  $r->get('/baz')->to(template => 'foo/bar');
-
-  # * /yada.txt  -> {controller => 'foo', action => 'yada', format => 'txt'}
-  # * /yada.json -> {controller => 'foo', action => 'yada', format => 'json'}
-  $r->any('/yada' => [format => [qw(txt json)]])->to('foo#yada');
+=head2 Catch-all route
 
-  # GET   /foo/bar  -> {controller => 'foo', action => 'bar'}
-  # PUT   /foo/baz  -> {controller => 'foo', action => 'baz'}
-  # PATCH /foo      -> {controller => 'foo', action => 'yada'}
-  my $foo = $r->any('/foo')->to('foo#');
-  $foo->get('/bar')->to('#bar');
-  $foo->put('/baz')->to('#baz');
-  $foo->patch->to('#yada');
-
-This makes the process of growing your L<Mojolicious::Lite> prototypes into
-full L<Mojolicious> applications very straightforward.
+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.
 
-  # POST /bar
-  $r->post('/bar' => sub {
-    my $c = shift;
-    $c->render(text => 'Just like a Mojolicious::Lite action.');
-  });
-
-Even the more abstract concepts are available with methods like
-L<Mojolicious::Routes::Route/"under">.
-
-  # GET  /yada
-  # POST /yada
-  my $yada = $r->under('/yada');
-  $yada->get(sub {
-    my $c = shift;
-    $c->render(text => 'Hello.');
-  });
-  $yada->post(sub {
-    my $c = shift;
-    $c->render(text => 'Go away.');
+  # * /*
+  $r->any('/*whatever' => {whatever => ''} => sub {
+    my $c        = shift;
+    my $whatever = $c->param('whatever');
+    $c->render(text => "/$whatever did not match.", status => 404);
   });
 
 =head2 Hooks
@@ -692,7 +669,7 @@ especially for plugins.
 
   1;
 
-Post-processing the response to set additional headers is a very common use.
+Post-processing the response to add or remove headers is a very common use.
 
   # Make sure static files are cached
   $app->hook(after_static => sub {
@@ -700,6 +677,12 @@ Post-processing the response to set additional headers is a very common use.
     $c->res->headers->cache_control('max-age=3600, must-revalidate');
   });
 
+  # Remove a default header
+  $app->hook(after_dispatch => sub {
+    my $c = shift;
+    $c->res->headers->remove('Server');
+  });
+
 Same for pre-processing the request.
 
   # Allow "_method" query parameter to override request method
@@ -742,6 +725,22 @@ You can even extend much of the core functionality.
 
 For a full list of available hooks see L<Mojolicious/"HOOKS">.
 
+=head2 Introspection
+
+The command L<Mojolicious::Command::routes> can be used from the command line
+to list all available routes together with name and underlying regular
+expressions.
+
+  $ ./myapp.pl routes -v
+  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+)  ^/?(?:\.([^/]+))?$
+  /bar        ..U.  *     bar      ^/bar
+    +/baz     ...W  GET   baz      ^/baz            ^/?(?:\.([^/]+))?$
+  /yada       ....  *     yada     ^/yada           ^/?(?:\.([^/]+))?$
+
+=head1 ADVANCED
+
+Less commonly used and more powerful features.
+
 =head2 Shortcuts
 
 You can also add your own shortcuts with L<Mojolicious::Routes/"add_shortcut">
@@ -752,7 +751,7 @@ to make route generation more expressive.
     my ($r, $name) = @_;
 
     # Generate "/$name" route
-    my $resource = $r->route("/$name")->to("$name#");
+    my $resource = $r->any("/$name")->to("$name#");
 
     # Handle POST requests
     $resource->post->to('#create')->name("create_$name");
@@ -772,36 +771,9 @@ to make route generation more expressive.
 
   # POST    /user -> {controller => 'user', action => 'create'}
   # GET     /user -> {controller => 'user', action => 'show'}
-  # OPTIONS /user
+  # OPTIONS /user -> {cb => sub {...}}
   $r->resource('user');
 
-Shortcuts can lead to anything, routes, bridges or maybe even both. And watch
-out for quicksand!
-
-=head2 Introspection
-
-The command L<Mojolicious::Command::routes> can be used from the command line
-to list all available routes together with name and underlying regular
-expressions.
-
-  $ ./myapp.pl routes -v
-  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+))(?:\.([^/]+)$)?
-  /bar        B...  *     bar      ^/bar
-    +/baz     ...W  GET   baz      ^/baz(?:\.([^/]+)$)?
-  /yada       ....  *     yada     ^/yada(?:\.([^/]+)$)?
-
-=head1 ADVANCED
-
-Less commonly used and more powerful features.
-
-=head2 IRIs
-
-IRIs are handled transparently, that means paths are guaranteed to be
-unescaped and decoded from bytes to characters.
-
-  # GET /☃ (unicode snowman) -> {controller => 'foo', action => 'snowman'}
-  $r->get('/☃')->to('foo#snowman');
-
 =head2 Rearranging routes
 
 Until the first request has been handled, all routes can still be moved around
@@ -904,7 +876,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 +899,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 {
@@ -8,11 +8,14 @@ Mojolicious::Guides - Mojolicious guide to the galaxy
 =head1 DON'T PANIC!
 
 We are constantly working on new documentation, follow us on
-L<Twitter|http://twitter.com/kraih>, L<GitHub|http://github.com/kraih/mojo>
-or join the official IRC channel C<#mojo> on C<irc.perl.org> to get all the
-latest updates.
+L<GitHub|http://github.com/kraih/mojo> or join the official IRC channel
+C<#mojo> on C<irc.perl.org> to get all the latest updates.
 
-=head1 LEARNING PERL
+=head1 BASICS
+
+=over 2
+
+=item Learning Perl
 
 If you are new to Perl, we recommend
 L<Learn Perl in 2 hours 30 minutes|http://qntm.org/files/perl/perl.html> for a
@@ -22,6 +25,14 @@ available in many formats. Both are excellent introductions to the language.
 For more books and documentation, check out
 L<learn.perl.org|http://learn.perl.org/>.
 
+=item Learning Web Technologies
+
+All web development starts with HTML, CSS and JavaScript, to learn the basics
+we recommend the
+L<Mozilla Developer Network|https://developer.mozilla.org/en-US/docs/Web>.
+
+=back
+
 =head1 TUTORIAL
 
 =over 2
@@ -44,7 +55,7 @@ therefore considered a prerequisite, you should definitely take a look!
 =item L<Mojolicious::Guides::Growing>
 
 Starting a L<Mojolicious::Lite> prototype from scratch and growing it into a
-well structured L<Mojolicious> application.
+well-structured L<Mojolicious> application.
 
 =item L<Mojolicious::Guides::Routing>
 
@@ -129,13 +140,15 @@ designed to be used with it and are being developed under the same umbrella.
 
 =over 2
 
-=item L<Mango>
+=item L<Mojo::Pg>
 
-Pure-Perl non-blocking I/O MongoDB driver.
+A tiny wrapper around L<DBD::Pg> that makes
+L<PostgreSQL|http://www.postgresql.org> a lot of fun to use with
+L<Mojolicious>.
 
 =item L<Minion>
 
-Job queue.
+A job queue for L<Mojolicious> with support for multiple backends.
 
 =back
 
@@ -287,8 +300,6 @@ This is the class hierarchy of the L<Mojolicious> distribution.
 
 =item * L<Mojo::IOLoop>
 
-=item * L<Mojo::JSON>
-
 =item * L<Mojo::JSON::Pointer>
 
 =item * L<Mojo::Loader>
@@ -425,6 +436,8 @@ This is the class hierarchy of the L<Mojolicious> distribution.
 
 =item * L<Mojo::DOM>
 
+=item * L<Mojo::JSON>
+
 =item * L<Mojo::Util>
 
 =item * L<ojo>
@@ -87,15 +87,15 @@ L<Mojolicious>.
 =head1 TUTORIAL
 
 A quick example driven introduction to the wonders of L<Mojolicious::Lite>.
-Most of what you'll learn here also applies to normal L<Mojolicious>
+Most of what you'll learn here also applies to full L<Mojolicious>
 applications.
 
 =head2 Hello World
 
 A simple Hello World application can look like this, L<strict>, L<warnings>,
-L<utf8> and Perl 5.10 features are automatically enabled and a few functions
-imported when you use L<Mojolicious::Lite>, turning your script into a full
-featured web application.
+L<utf8> and Perl 5.10 features are automatically enabled and a few
+L</"FUNCTIONS"> imported when you use L<Mojolicious::Lite>, turning your
+script into a full featured web application.
 
   #!/usr/bin/env perl
   use Mojolicious::Lite;
@@ -144,7 +144,7 @@ Your application will automatically reload itself if you start it with the
 C<morbo> development web server, so you don't have to restart the server after
 every change.
 
-  $ morbo myapp.pl
+  $ morbo ./myapp.pl
   Server available at http://127.0.0.1:3000.
 
 For more information about how to deploy your application see also
@@ -262,11 +262,9 @@ only the information you're actually interested in.
 =head2 Route names
 
 All routes can have a name associated with them, this allows automatic
-template detection and back referencing with
-L<Mojolicious::Controller/"url_for"> as well as many helpers like
-L<Mojolicious::Plugin::TagHelpers/"link_to">. Nameless routes get an
-automatically generated one assigned that is simply equal to the route itself
-without non-word characters.
+template detection and backreferencing with
+L<Mojolicious::Controller/"url_for">, on which many methods and helpers like
+L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
 
   use Mojolicious::Lite;
 
@@ -289,6 +287,9 @@ without non-word characters.
   @@ hello.html.ep
   Hello World!
 
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters.
+
 =head2 Layouts
 
 Templates can have layouts too, you just select one with the helper
@@ -347,9 +348,8 @@ many helpers.
 
 =head2 Helpers
 
-You can also extend L<Mojolicious> with your own helpers, a list of all
-built-in ones can be found in L<Mojolicious::Plugin::DefaultHelpers> and
-L<Mojolicious::Plugin::TagHelpers>.
+Helpers are little functions you can reuse throughout your whole application,
+from actions to templates.
 
   use Mojolicious::Lite;
 
@@ -374,6 +374,9 @@ L<Mojolicious::Plugin::TagHelpers>.
   @@ secret.html.ep
   We know who you are <%= whois %>.
 
+A list of all built-in ones can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
 =head2 Placeholders
 
 Route placeholders allow capturing parts of a request path until a C</> or
@@ -472,8 +475,7 @@ Routes can be restricted to specific request methods with different keywords.
 =head2 Optional placeholders
 
 All placeholders require a value, but by assigning them default values you can
-make capturing optional. Default values that don't belong to a placeholder
-simply get merged into the stash all the time.
+make capturing optional.
 
   use Mojolicious::Lite;
 
@@ -490,6 +492,9 @@ simply get merged into the stash all the time.
   @@ groovy.txt.ep
   My name is <%= $name %> and it is <%= $day %>.
 
+Default values that don't belong to a placeholder simply get merged into the
+stash all the time.
+
 =head2 Restrictive placeholders
 
 The easiest way to make placeholders more restrictive are alternatives, you
@@ -529,8 +534,8 @@ is fine though.
 =head2 Under
 
 Authentication and code shared between multiple routes can be realized easily
-with bridge routes generated by the L</"under"> statement. All following
-routes are only evaluated if the callback returned a true value.
+with routes generated by the L</"under"> statement. All following routes are
+only evaluated if the callback returned a true value.
 
   use Mojolicious::Lite;
 
@@ -615,10 +620,12 @@ L</"under"> statements.
 
 =head2 Formats
 
-Formats can be automatically detected by looking at file extensions.
+Formats can be automatically detected from file extensions, they are used to
+find the right template and generate the correct C<Content-Type> header.
 
   use Mojolicious::Lite;
 
+  # /detection
   # /detection.html
   # /detection.txt
   get '/detection' => sub {
@@ -639,7 +646,8 @@ Formats can be automatically detected by looking at file extensions.
   @@ detected.txt.ep
   TXT was detected.
 
-Restrictive placeholders can also be used.
+The default format is C<html>, restrictive placeholders can be used to limit
+possible values.
 
   use Mojolicious::Lite;
 
@@ -725,7 +733,12 @@ 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.
+Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
+headers is supported as well and can be tested very easily with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
 
 =head2 External templates
 
@@ -861,7 +874,7 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
     my $c   = shift;
     my $url = $c->param('url') || 'http://mojolicio.us';
     my $dom = $c->ua->get($url)->res->dom;
-    $c->render(json => [$dom->find('h1, h2, h3')->text->each]);
+    $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
   };
 
   # Non-blocking
@@ -885,8 +898,8 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
       sub {
         my ($delay, $mojo, $cpan) = @_;
         $c->render(json => {
-          mojo => $mojo->res->dom->html->head->title->text,
-          cpan => $cpan->res->dom->html->head->title->text
+          mojo => $mojo->res->dom->at('title')->text,
+          cpan => $cpan->res->dom->at('title')->text
         });
       }
     );
@@ -1013,6 +1026,7 @@ automatically exported.
   my $route = any '/:foo' => {foo => 'bar'} => sub {...};
   my $route = any '/:foo' => [foo => qr/\w+/] => sub {...};
   my $route = any [qw(GET POST)] => '/:foo' => sub {...};
+  my $route = any [qw(GET POST)] => '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"any">, matching any of the
 listed HTTP request methods or all. See also the tutorial above for many more
@@ -1022,7 +1036,12 @@ argument variations.
 
   my $app = app;
 
-The L<Mojolicious::Lite> application.
+Returns the L<Mojolicious::Lite> application object, which is a subclass of
+L<Mojolicious>.
+
+  # Use all the available attributes and methods
+  app->log->level('error');
+  app->defaults(foo => 'bar');
 
 =head2 del
 
@@ -1108,12 +1127,13 @@ requests. See also the tutorial above for many more argument variations.
 
 =head2 under
 
-  my $bridge = under sub {...};
-  my $bridge = under '/:foo' => sub {...};
-  my $bridge = under '/:foo' => [foo => qr/\w+/];
-  my $bridge = under {format => 0};
+  my $route = under sub {...};
+  my $route = under '/:foo' => sub {...};
+  my $route = under '/:foo' => {foo => 'bar'};
+  my $route = under '/:foo' => [foo => qr/\w+/];
+  my $route = under [format => 0];
 
-Generate bridge route with L<Mojolicious::Routes::Route/"under">, to which all
+Generate nested route with L<Mojolicious::Routes::Route/"under">, to which all
 following routes are automatically appended. See also the tutorial above for
 more argument variations.
 
@@ -17,7 +17,7 @@ sub parse {
   my $config
     = eval 'package Mojolicious::Plugin::Config::Sandbox; no warnings;'
     . "sub app; local *app = sub { \$app }; use Mojo::Base -strict; $content";
-  die qq{Couldn't load configuration from file "$file": $@} if !$config && $@;
+  die qq{Can't load configuration from file "$file": $@} if !$config && $@;
   die qq{Configuration file "$file" did not return a hash reference.\n}
     unless ref $config eq 'HASH';
 
@@ -3,6 +3,7 @@ use Mojo::Base 'Mojolicious::Plugin';
 
 use Mojo::ByteStream;
 use Mojo::Collection;
+use Mojo::Exception;
 use Mojo::IOLoop;
 use Mojo::Util qw(dumper sha1_sum steady_time);
 
@@ -16,31 +17,32 @@ sub register {
 
   # Stash key shortcuts (should not generate log messages)
   for my $name (qw(extends layout title)) {
-    $app->helper(
-      $name => sub {
-        my $c     = shift;
-        my $stash = $c->stash;
-        $stash->{$name} = shift if @_;
-        $c->stash(@_) if @_;
-        return $stash->{$name};
-      }
-    );
+    $app->helper($name => sub { shift->stash($name, @_) });
   }
 
-  $app->helper($_ => $self->can("_$_"))
-    for qw(accepts content content_for csrf_token current_route delay),
-    qw(inactivity_timeout url_with);
-  $app->helper(b => sub { shift; Mojo::ByteStream->new(@_) });
-  $app->helper(c => sub { shift; Mojo::Collection->new(@_) });
+  $app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) });
+  $app->helper(b       => sub { shift; Mojo::ByteStream->new(@_) });
+  $app->helper(c       => sub { shift; Mojo::Collection->new(@_) });
   $app->helper(config  => sub { shift->app->config(@_) });
-  $app->helper(dumper  => sub { shift; dumper(@_) });
+
+  $app->helper($_ => $self->can("_$_"))
+    for qw(content content_for csrf_token current_route delay),
+    qw(inactivity_timeout is_fresh url_with);
+
+  $app->helper(dumper => sub { shift; dumper(@_) });
   $app->helper(include => sub { shift->render_to_string(@_) });
-  $app->helper(ua      => sub { shift->app->ua });
+
+  $app->helper("reply.$_" => $self->can("_$_")) for qw(asset static);
+
+  $app->helper('reply.exception' => sub { _development('exception', @_) });
+  $app->helper('reply.not_found' => sub { _development('not_found', @_) });
+  $app->helper(ua                => sub { shift->app->ua });
 }
 
-sub _accepts {
+sub _asset {
   my $c = shift;
-  return $c->app->renderer->accepts($c, @_);
+  $c->app->static->serve_asset($c, @_);
+  $c->rendered;
 }
 
 sub _content {
@@ -70,16 +72,59 @@ sub _csrf_token {
 }
 
 sub _current_route {
-  return '' unless my $endpoint = shift->match->endpoint;
-  return $endpoint->name unless @_;
-  return $endpoint->name eq shift;
+  return '' unless my $route = shift->match->endpoint;
+  return @_ ? $route->name eq shift : $route->name;
 }
 
 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 _development {
+  my ($page, $c, $e) = @_;
+
+  my $app = $c->app;
+  $app->log->error($e = Mojo::Exception->new($e)) if $page eq 'exception';
+
+  # Filtered stash snapshot
+  my $stash = $c->stash;
+  my %snapshot = map { $_ => $stash->{$_} }
+    grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
+
+  # Render with fallbacks
+  my $mode     = $app->mode;
+  my $renderer = $app->renderer;
+  my $options  = {
+    exception => $page eq 'exception' ? $e : undef,
+    format => $stash->{format} || $renderer->default_format,
+    handler  => undef,
+    snapshot => \%snapshot,
+    status   => $page eq 'exception' ? 500 : 404,
+    template => "$page.$mode"
+  };
+  my $inline = $renderer->_bundled($mode eq 'development' ? $mode : $page);
+  return $c if _fallbacks($c, $options, $page, $inline);
+  _fallbacks($c, {%$options, format => 'html'}, $page, $inline);
+  return $c;
+}
+
+sub _fallbacks {
+  my ($c, $options, $template, $inline) = @_;
+
+  # Mode specific template
+  return 1 if $c->render_maybe(%$options);
+
+  # Normal template
+  return 1 if $c->render_maybe(%$options, template => $template);
+
+  # Inline template
+  my $stash = $c->stash;
+  return undef unless $stash->{format} eq 'html';
+  delete @$stash{qw(extends layout)};
+  return $c->render_maybe(%$options, inline => $inline, handler => 'ep');
 }
 
 sub _inactivity_timeout {
@@ -87,6 +132,18 @@ sub _inactivity_timeout {
   $stream->timeout(shift);
 }
 
+sub _is_fresh {
+  my ($c, %options) = @_;
+  return $c->app->static->is_fresh($c, \%options);
+}
+
+sub _static {
+  my ($c, $file) = @_;
+  return !!$c->rendered if $c->app->static->serve($c, $file);
+  $c->app->log->debug(qq{File "$file" not found, public directory missing?});
+  return !$c->render_not_found;
+}
+
 sub _url_with {
   my $c = shift;
   return $c->url_for(@_)->query($c->req->url->query->clone);
@@ -110,7 +167,7 @@ Mojolicious::Plugin::DefaultHelpers - Default helpers plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::DefaultHelpers> is a collection of renderer helpers for
+L<Mojolicious::Plugin::DefaultHelpers> is a collection of helpers for
 L<Mojolicious>.
 
 This is a core plugin, that means it is always enabled and its code a good
@@ -225,13 +282,27 @@ Check or get name of current route.
 Disable automatic rendering and use L<Mojo::IOLoop/"delay"> to manage
 callbacks and control the flow of events, which can help you avoid deep nested
 closures and memory leaks that often result from continuation-passing style.
-Calls L<Mojolicious::Controller/"render_exception"> if an error occured in one
-of the steps, breaking the chain.
+Also keeps a reference to L<Mojolicious::Controller/"tx"> in case the
+underlying connection gets closed early, and calls L</"reply-E<gt>exception">
+if an exception gets thrown in one of the steps, breaking the chain.
 
   # Longer version
   $c->render_later;
+  my $tx    = $c->tx;
   my $delay = Mojo::IOLoop->delay(sub {...}, sub {...});
-  $delay->catch(sub { $c->render_exception(pop) })->wait;
+  $delay->catch(sub { $c->reply->exception(pop) and undef $tx })->wait;
+
+  # Non-blocking request
+  $c->delay(
+    sub {
+      my $delay = shift;
+      $c->ua->get('http://mojolicio.us' => $delay->begin);
+    },
+    sub {
+      my ($delay, $tx) = @_;
+      $c->render(json => {title => $tx->res->dom->at('title')->text});
+    }
+  );
 
 =head2 dumper
 
@@ -268,7 +339,22 @@ timeout if possible.
   %= include 'menubar'
   %= include 'menubar', format => 'txt'
 
-Alias for C<Mojolicious::Controller/"render_to_string">.
+Alias for L<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
 
@@ -284,6 +370,58 @@ L</"stash">.
 
 Alias for L<Mojolicious::Controller/"param">.
 
+=head2 reply->asset
+
+  $c->reply->asset(Mojo::Asset::File->new);
+
+Reply with a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object using
+L<Mojolicious::Static/"serve_asset">, and perform content negotiation with
+C<Range>, C<If-Modified-Since> and C<If-None-Match> headers.
+
+  # Serve asset with custom modification time
+  my $asset = Mojo::Asset::Memory->new;
+  $asset->add_chunk('Hello World!')->mtime(784111777);
+  $c->res->headers->content_type('text/plain');
+  $c->reply->asset($asset);
+
+  # Serve static file if it exists
+  if (my $asset = $c->app->static->file('images/logo.png')) {
+    $c->res->headers->content_type('image/png');
+    $c->reply->asset($asset);
+  }
+
+=head2 reply->exception
+
+  $c = $c->reply->exception('Oops!');
+  $c = $c->reply->exception(Mojo::Exception->new('Oops!'));
+
+Render the exception template C<exception.$mode.$format.*> or
+C<exception.$format.*> and set the response status code to C<500>. Also sets
+the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot>
+to a copy of the L</"stash"> for use in the templates.
+
+=head2 reply->not_found
+
+  $c = $c->reply->not_found;
+
+Render the not found template C<not_found.$mode.$format.*> or
+C<not_found.$format.*> and set the response status code to C<404>. Also sets
+the stash value C<snapshot> to a copy of the L</"stash"> for use in the
+templates.
+
+=head2 reply->static
+
+  my $bool = $c->reply->static('images/logo.png');
+  my $bool = $c->reply->static('../lib/MyApp.pm');
+
+Reply with a static file using L<Mojolicious::Static/"serve">, usually from
+the C<public> directories or C<DATA> sections of your application. Note that
+this helper does not protect from traversing to parent directories.
+
+  # Serve file with a custom content type
+  $c->res->headers->content_type('application/myapp');
+  $c->reply->static('foo.txt');
+
 =head2 session
 
   %= session 'foo'
@@ -301,11 +439,11 @@ Alias for L<Mojolicious::Controller/"stash">.
 
 =head2 title
 
+  %= title
   % title 'Welcome!';
   % title 'Welcome!', foo => 'bar';
-  %= title
 
-Set C<title> stash value, all additional pairs get merged into the
+Get or set C<title> stash value, all additional pairs get merged into the
 L</"stash">.
 
 =head2 ua
@@ -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,45 +4,47 @@ 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;
+        my $prepend = 'my $self = my $c = shift; my $_S = $c->stash; {';
+        $prepend .= join '', map {" my \$$_ = \$_S->{'$_'};"} @keys;
+        $mt->prepend($prepend . $mt->prepend)->append('}' . $mt->append);
 
-        $cache->set($key => $mt->prepend($prepend . $mt->prepend));
+        $cache->set($key => $mt);
       }
 
       # 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 +53,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 +82,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.
@@ -1,18 +1,15 @@
 package Mojolicious::Plugin::JSONConfig;
 use Mojo::Base 'Mojolicious::Plugin::Config';
 
-use Mojo::JSON;
+use Mojo::JSON 'from_json';
 use Mojo::Template;
-use Mojo::Util 'encode';
 
 sub parse {
   my ($self, $content, $file, $conf, $app) = @_;
 
-  my $json   = Mojo::JSON->new;
-  my $config = $json->decode($self->render($content, $file, $conf, $app));
-  my $err    = $json->error;
-  die qq{Couldn't parse config "$file": $err} if !$config && $err;
-  die qq{Invalid config "$file"} if !$config || ref $config ne 'HASH';
+  my $config = eval { from_json $self->render($content, $file, $conf, $app) };
+  die qq{Can't parse config "$file": $@} if !$config && $@;
+  die qq{Invalid config "$file"} unless ref $config eq 'HASH';
 
   return $config;
 }
@@ -26,10 +23,9 @@ sub render {
   my $prepend = q[my $app = shift; no strict 'refs'; no warnings 'redefine';];
   $prepend .= q[sub app; local *app = sub { $app }; use Mojo::Base -strict;];
 
-  # Render and encode for JSON decoding
   my $mt = Mojo::Template->new($conf->{template} || {})->name($file);
-  my $json = $mt->prepend($prepend . $mt->prepend)->render($content, $app);
-  return ref $json ? die $json : encode 'UTF-8', $json;
+  my $output = $mt->prepend($prepend . $mt->prepend)->render($content, $app);
+  return ref $output ? die $output : $output;
 }
 
 1;
@@ -17,9 +17,7 @@ sub register {
   }
 
   my $route = $app->routes->route($path)->detour(app => $embed);
-  $route->over(host => $host) if $host;
-
-  return $route;
+  return $host ? $route->over(host => $host) : $route;
 }
 
 1;
@@ -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;
 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!^https://metacpan\.org/pod/!$perldoc!
+    and $_->{href} =~ s!::!/!gi
+    for $dom->find('a[href]')->map('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("https://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('https://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;
 }
 
@@ -2,7 +2,7 @@ package Mojolicious::Plugin::TagHelpers;
 use Mojo::Base 'Mojolicious::Plugin';
 
 use Mojo::ByteStream;
-use Mojo::Util 'xml_escape';
+use Mojo::Util 'xss_escape';
 use Scalar::Util 'blessed';
 
 sub register {
@@ -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 {
@@ -76,7 +76,7 @@ sub _input {
   my %attrs = @_ % 2 ? (value => shift, @_) : @_;
 
   # Special selection value
-  my @values = $c->param($name);
+  my @values = @{$c->every_param($name)};
   my $type = $attrs{type} || '';
   if (@values && $type ne 'submit') {
 
@@ -140,7 +140,7 @@ sub _option {
   $attrs{selected} = 'selected' if exists $values->{$pair->[1]};
   %attrs = (%attrs, @$pair[2 .. $#$pair]);
 
-  return _tag('option', %attrs, sub { xml_escape $pair->[0] });
+  return _tag('option', %attrs, $pair->[0]);
 }
 
 sub _password_field {
@@ -152,7 +152,7 @@ sub _password_field {
 sub _select_field {
   my ($c, $name, $options, %attrs) = (shift, shift, shift, @_);
 
-  my %values = map { $_ => 1 } $c->param($name);
+  my %values = map { $_ => 1 } @{$c->every_param($name)};
 
   my $groups = '';
   for my $group (@$options) {
@@ -212,13 +212,13 @@ sub _tag {
     }
     delete $attrs{data};
   }
-  $tag .= qq{ $_="} . xml_escape($attrs{$_} // '') . '"' for sort keys %attrs;
+  $tag .= qq{ $_="} . xss_escape($attrs{$_} // '') . '"' for sort keys %attrs;
 
   # Empty element
   unless ($cb || defined $content) { $tag .= ' />' }
 
   # End tag
-  else { $tag .= '>' . ($cb ? $cb->() : xml_escape($content)) . "</$name>" }
+  else { $tag .= '>' . ($cb ? $cb->() : xss_escape $content) . "</$name>" }
 
   # Prevent escaping
   return Mojo::ByteStream->new($tag);
@@ -234,19 +234,17 @@ sub _tag_with_error {
 sub _text_area {
   my ($c, $name) = (shift, shift);
 
-  # Make sure content is wrapped
-  my $cb = ref $_[-1] eq 'CODE' ? pop : sub {''};
+  my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
   my $content = @_ % 2 ? shift : undef;
-  $cb = sub { xml_escape $content }
-    if defined($content = $c->param($name) // $content);
+  $content = $c->param($name) // $content // $cb // '';
 
-  return _validation($c, $name, 'textarea', @_, name => $name, $cb);
+  return _validation($c, $name, 'textarea', @_, name => $name, $content);
 }
 
 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;
@@ -686,12 +684,13 @@ HTML/XML tag generator.
 
 Very useful for reuse in more specific tag helpers.
 
-  $c->tag('div');
-  $c->tag('div', id => 'foo');
-  $c->tag(div => sub { 'Content' });
+  my $output = $c->tag('div');
+  my $output = $c->tag('div', id => 'foo');
+  my $output = $c->tag(div => '<p>This will be escaped</p>');
+  my $output = $c->tag(div => sub { '<p>This will not be escaped</p>' });
 
 Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent
-accidental double escaping.
+accidental double escaping in C<ep> templates.
 
 =head2 tag_with_error
 
@@ -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,22 +25,17 @@ 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;
 }
 
 sub load_plugin {
   my ($self, $name) = @_;
 
-  # Try all namespaces
-  my $class = $name =~ /^[a-z]/ ? camelize($name) : $name;
-  for my $ns (@{$self->namespaces}) {
-    my $module = "${ns}::$class";
-    return $module->new if _load($module);
-  }
-
-  # Full module name
-  return $name->new if _load($name);
+  # Try all namespaces and full module name
+  my $suffix = $name =~ /^[a-z]/ ? camelize($name) : $name;
+  my @classes = map {"${_}::$suffix"} @{$self->namespaces};
+  for my $class (@classes, $name) { return $class->new if _load($class) }
 
   # Not found
   die qq{Plugin "$name" missing, maybe you need to install it?\n};
@@ -52,10 +47,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 +64,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 \(my $dummy = 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,
@@ -146,14 +158,15 @@ sub template_for {
     if $controller && $action;
 
   # Try the route name if we don't have controller and action
-  return undef unless my $endpoint = $c->match->endpoint;
-  return $endpoint->name;
+  return undef unless my $route = $c->match->endpoint;
+  return $route->name;
 }
 
 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,89 +10,84 @@ 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;
-  if ($name && $name eq 'current' || !$name) {
-    return {} unless $endpoint = $self->endpoint;
+  my $route;
+  if (!$name || $name eq 'current') {
+    return {} unless $route = $self->endpoint;
   }
 
   # Find endpoint
-  else { return {path => $name} unless $endpoint = $self->root->lookup($name) }
+  else { return {path => $name} unless $route = $self->root->lookup($name) }
 
   # Merge values (clear format)
   my $captures = $self->stack->[-1] || {};
   %values = (%$captures, format => undef, %values);
-  my $pattern = $endpoint->pattern;
+  my $pattern = $route->pattern;
   $values{format}
     //= defined $captures->{format}
     ? $captures->{format}
     : $pattern->defaults->{format}
     if $pattern->constraints->{format};
 
-  my $path = $endpoint->render('', \%values);
-  return {path => $path, websocket => $endpoint->has_websocket};
+  my $path = $route->render(\%values);
+  return {path => $path, websocket => $route->has_websocket};
 }
 
 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 undef
+    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;
-  return if $methods && !grep { $_ eq $options->{method} } @$methods;
+  return undef if $methods && !grep { $_ eq $options->{method} } @$methods;
 
   # Conditions
   if (my $over = $r->over) {
     my $conditions = $self->{conditions} ||= $self->root->conditions;
     for (my $i = 0; $i < @$over; $i += 2) {
-      return unless my $condition = $conditions->{$over->[$i]};
-      return if !$condition->($r, $c, $captures, $over->[$i + 1]);
+      return undef unless my $condition = $conditions->{$over->[$i]};
+      return undef if !$condition->($r, $c, $captures, $over->[$i + 1]);
     }
   }
 
   # WebSocket
-  return if $r->is_websocket && !$options->{websocket};
+  return undef if $r->is_websocket && !$options->{websocket};
 
   # 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;
+  # Endpoint (or intermediate destination)
   if (($endpoint && $empty) || $r->inline) {
     push @{$self->stack}, {%$captures};
     if ($endpoint && $empty) {
       my $format = $captures->{format};
       if ($format) { $_->{format} = $format for @{$self->stack} }
-      return $self->endpoint($r);
+      return !!$self->endpoint($r);
     }
     delete @$captures{qw(app cb)};
   }
 
   # Match children
-  my $snapshot = [@{$self->stack}];
+  my @snapshot = $r->parent ? ([@{$self->stack}], $captures) : ([], {});
   for my $child (@{$r->children}) {
-    $self->_match($child, $c, $options);
-
-    # Endpoint found
-    return if $self->endpoint;
-
-    # Reset
-    if   ($r->parent) { $self->stack([@$snapshot])->{captures} = $captures }
-    else              { $self->stack([])->{captures}           = {} }
+    return 1 if $self->_match($child, $c, $options);
+    $self->stack([@{$snapshot[0]}])->{captures} = $snapshot[1];
   }
 }
 
@@ -144,11 +139,11 @@ Current position on the L</"stack">, defaults to C<0>.
 
 =head2 endpoint
 
-  my $endpoint = $match->endpoint;
-  $match       = $match->endpoint(Mojolicious::Routes::Route->new);
+  my $route = $match->endpoint;
+  $match    = $match->endpoint(Mojolicious::Routes::Route->new);
 
 The route endpoint that matched, usually a L<Mojolicious::Routes::Route>
-objects.
+object.
 
 =head2 root
 
@@ -160,7 +155,7 @@ The root of the route structure, usually a L<Mojolicious::Routes> object.
 =head2 stack
 
   my $stack = $match->stack;
-  $match    = $match->stack([{foo => 'bar'}]);
+  $match    = $match->stack([{action => 'foo'}, {action => 'bar'}]);
 
 Captured parameters with nesting history.
 
@@ -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;
 }
 
@@ -59,31 +57,26 @@ sub parse {
 }
 
 sub render {
-  my ($self, $values, $render) = @_;
-
-  # Merge values with defaults
-  my $format = ($values ||= {})->{format};
-  my $defaults = $self->defaults;
-  $values = {%$defaults, %$values};
+  my ($self, $values, $endpoint) = @_;
 
   # 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 = '' }
     }
@@ -92,8 +85,7 @@ sub render {
   }
 
   # Format can be optional
-  $str ||= '/';
-  return $render && $format ? "$str.$format" : $str;
+  return $endpoint && $format ? "$str.$format" : $str;
 }
 
 sub _compile {
@@ -106,35 +98,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 +137,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 {
@@ -213,9 +204,15 @@ sub _tokenize {
     # Placeholder, relaxed or wildcard
     elsif ($inside) { $tree[-1][-1] .= $char }
 
-    # Text
+    # Text (optimize slash+text and *+text+slash+text)
     elsif ($tree[-1][0] eq 'text') { $tree[-1][-1] .= $char }
-    else                           { push @tree, ['text', $char] }
+    elsif (!$tree[-2] && $tree[-1][0] eq 'slash') {
+      @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 +268,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 +318,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.
@@ -1,9 +1,10 @@
 package Mojolicious::Routes::Route;
 use Mojo::Base -base;
 
-use Carp 'croak';
+use Carp ();
+use Mojo::Util;
 use Mojolicious::Routes::Pattern;
-use Scalar::Util qw(blessed weaken);
+use Scalar::Util ();
 
 has [qw(inline parent partial)];
 has 'children' => sub { [] };
@@ -12,19 +13,19 @@ has pattern    => sub { Mojolicious::Routes::Pattern->new };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
   # Call shortcut with current route
-  croak qq{Can't locate object method "$method" via package "$package"}
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
     unless my $shortcut = $self->root->shortcuts->{$method};
   return $self->$shortcut(@_);
 }
 
 sub add_child {
   my ($self, $route) = @_;
-  weaken $route->remove->parent($self)->{parent};
+  Scalar::Util::weaken $route->remove->parent($self)->{parent};
   push @{$self->children}, $route;
   return $self;
 }
@@ -57,20 +58,12 @@ sub find {
 
 sub get { shift->_generate_route(GET => @_) }
 
-sub has_conditions {
-  my $self = shift;
-  return 1 if @{$self->over || []};
-  return undef unless my $parent = $self->parent;
-  return $parent->has_conditions;
-}
-
 sub has_custom_name { !!shift->{custom} }
 
 sub has_websocket {
   my $self = shift;
-  return 1 if $self->is_websocket;
-  return undef unless my $parent = $self->parent;
-  return $parent->is_websocket;
+  return $self->{has_websocket} if exists $self->{has_websocket};
+  return $self->{has_websocket} = grep { $_->is_websocket } @{$self->_chain};
 }
 
 sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }
@@ -96,7 +89,7 @@ sub over {
   my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
   return $self unless @$conditions;
   $self->{over} = $conditions;
-  $self->root->cache(0);
+  $self->root->cache->max_keys(0);
 
   return $self;
 }
@@ -120,22 +113,14 @@ 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;
-
-  # Let parent render
-  return $parent ? $parent->render($path, $values) : $path;
+  my ($self, $values) = @_;
+  my $path = join '',
+    map { $_->pattern->render($values, !@{$_->children} && !$_->partial) }
+    @{$self->_chain};
+  return $path || '/';
 }
 
-sub root {
-  my $root = my $parent = shift;
-  $root = $parent while $parent = $parent->parent;
-  return $root;
-}
+sub root { shift->_chain->[0] }
 
 sub route {
   my $self   = shift;
@@ -150,7 +135,7 @@ sub to {
 
   my $pattern = $self->pattern;
   return $pattern->defaults unless @_;
-  my ($shortcut, %defaults) = _values(@_);
+  my ($shortcut, %defaults) = Mojo::Util::_options(@_);
 
   if ($shortcut) {
 
@@ -166,16 +151,13 @@ sub to {
     }
   }
 
-  $pattern->defaults({%{$pattern->defaults}, %defaults});
+  @{$pattern->defaults}{keys %defaults} = values %defaults;
 
   return $self;
 }
 
 sub to_string {
-  my $self = shift;
-  my $pattern = $self->parent ? $self->parent->to_string : '';
-  $pattern .= $self->pattern->pattern if $self->pattern->pattern;
-  return $pattern;
+  join '', map { $_->pattern->pattern // '' } @{shift->_chain};
 }
 
 sub under { shift->_generate_route(under => @_) }
@@ -194,6 +176,12 @@ sub websocket {
   return $route;
 }
 
+sub _chain {
+  my @chain = (my $parent = shift);
+  unshift @chain, $parent while $parent = $parent->parent;
+  return \@chain;
+}
+
 sub _generate_route {
   my ($self, $methods, @args) = @_;
 
@@ -219,28 +207,13 @@ sub _generate_route {
     elsif (ref $arg eq 'HASH') { %defaults = (%defaults, %$arg) }
   }
 
-  # Create bridge or route
   my $route
-    = $methods eq 'under'
-    ? $self->bridge($pattern, @constraints)
-    : $self->route($pattern, @constraints)->via($methods);
-  $route->over(\@conditions)->to(\%defaults);
+    = $self->route($pattern, @constraints)->over(\@conditions)->to(\%defaults);
+  $methods eq 'under' ? $route->inline(1) : $route->via($methods);
 
   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
@@ -276,7 +249,7 @@ The children of this route, used for nesting routes.
   my $bool = $r->inline;
   $r       = $r->inline($bool);
 
-Allow L</"bridge"> semantics for this route.
+Allow L</"under"> semantics for this route.
 
 =head2 parent
 
@@ -321,6 +294,7 @@ current parent if necessary.
   my $route = $r->any('/:foo' => {foo => 'bar'} => sub {...});
   my $route = $r->any('/:foo' => [foo => qr/\w+/] => sub {...});
   my $route = $r->any([qw(GET POST)] => '/:foo' => sub {...});
+  my $route = $r->any([qw(GET POST)] => '/:foo' => [foo => qr/\w+/]);
 
 Generate L<Mojolicious::Routes::Route> object matching any of the listed HTTP
 request methods or all. See also the L<Mojolicious::Lite> tutorial for many
@@ -330,13 +304,13 @@ more argument variations.
 
 =head2 bridge
 
-  my $bridge = $r->bridge;
-  my $bridge = $r->bridge('/:action');
-  my $bridge = $r->bridge('/:action', action => qr/\w+/);
-  my $bridge = $r->bridge(format => 0);
+  my $route = $r->bridge;
+  my $route = $r->bridge('/:action');
+  my $route = $r->bridge('/:action', action => qr/\w+/);
+  my $route = $r->bridge(format => 0);
 
-Low-level generator for bridge routes, returns a L<Mojolicious::Routes::Route>
-object.
+Low-level generator for nested routes with their own intermediate destination,
+returns a L<Mojolicious::Routes::Route> object.
 
   my $auth = $r->bridge('/user')->to('user#auth');
   $auth->get('/show')->to('#show');
@@ -386,12 +360,6 @@ See also the L<Mojolicious::Lite> tutorial for many more argument variations.
 
   $r->get('/user')->to('user#show');
 
-=head2 has_conditions
-
-  my $bool = $r->has_conditions;
-
-Check if this route has active conditions.
-
 =head2 has_custom_name
 
   my $bool = $r->has_custom_name;
@@ -402,7 +370,8 @@ Check if this route has a custom name.
 
   my $bool = $r->has_websocket;
 
-Check if this route has a WebSocket ancestor.
+Check if this route has a WebSocket ancestor and cache the result for future
+checks.
 
 =head2 is_endpoint
 
@@ -518,8 +487,7 @@ Remove route from parent.
 
 =head2 render
 
-  my $path = $r->render($suffix);
-  my $path = $r->render($suffix, {foo => 'bar'});
+  my $path = $r->render({foo => 'bar'});
 
 Render route with parameters into a path.
 
@@ -527,9 +495,7 @@ Render route with parameters into a path.
 
   my $root = $r->root;
 
-The L<Mojolicious::Routes> object this route is an descendent of.
-
-  $r->root->cache(0);
+The L<Mojolicious::Routes> object this route is a descendant of.
 
 =head2 route
 
@@ -566,13 +532,15 @@ Stringify the whole route.
 
 =head2 under
 
-  my $bridge = $r->under(sub {...});
-  my $bridge = $r->under('/:foo' => sub {...});
-  my $bridge = $r->under('/:foo' => [foo => qr/\w+/]);
-  my $bridge = $r->under({format => 0});
+  my $route = $r->under(sub {...});
+  my $route = $r->under('/:foo' => sub {...});
+  my $route = $r->under('/:foo' => {foo => 'bar'});
+  my $route = $r->under('/:foo' => [foo => qr/\w+/]);
+  my $route = $r->under([format => 0]);
 
-Generate L<Mojolicious::Routes::Route> object for bridge route. See also the
-L<Mojolicious::Lite> tutorial for many more argument variations.
+Generate L<Mojolicious::Routes::Route> object for a nested route with its own
+intermediate destination. See also the L<Mojolicious::Lite> tutorial for many
+more argument variations.
 
   my $auth = $r->under('/user')->to('user#auth');
   $auth->get('/show')->to('#show');
@@ -608,11 +576,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];
@@ -87,17 +86,15 @@ sub match {
   my $match = Mojolicious::Routes::Match->new(root => $self);
   $c->match($match);
   my $cache = $self->cache;
-  my $cached = $cache ? $cache->get("$method:$path:$ws") : undef;
-  return $match->endpoint($cached->{endpoint})->stack($cached->{stack})
-    if $cached;
+  if (my $result = $cache->get("$method:$path:$ws")) {
+    return $match->endpoint($result->{endpoint})->stack($result->{stack});
+  }
 
   # Check routes
   $match->match($c => {method => $method, path => $path, websocket => $ws});
-
-  # Cache routes without conditions
-  return unless $cache && (my $endpoint = $match->endpoint);
-  my $result = {endpoint => $endpoint, stack => $match->stack};
-  $cache->set("$method:$path:$ws" => $result) unless $endpoint->has_conditions;
+  return unless my $route = $match->endpoint;
+  $cache->set(
+    "$method:$path:$ws" => {endpoint => $route, stack => $match->stack});
 }
 
 sub route {
@@ -145,11 +142,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);
@@ -173,7 +167,7 @@ sub _controller {
   my $class = ref $new;
   my $app   = $old->app;
   my $log   = $app->log;
-  if (my $sub = $new->can('handler')) {
+  if ($new->isa('Mojo')) {
     $log->debug(qq{Routing to application "$class".});
 
     # Try to connect routes
@@ -181,7 +175,7 @@ sub _controller {
       my $r = $new->$sub;
       weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent;
     }
-    $new->$sub($old);
+    $new->handler($old);
     $old->stash->{'mojo.routed'}++;
   }
 
@@ -263,9 +257,6 @@ L<Mojolicious::Controller> and L<Mojo>.
 
 Routing cache, defaults to a L<Mojo::Cache> object.
 
-  # Disable caching
-  $r->cache(0);
-
 =head2 conditions
 
   my $conditions = $r->conditions;
@@ -289,7 +280,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
 
@@ -7,23 +7,28 @@ 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 { [] };
 
-# Last modified default
-my $MTIME = time;
-
 # Bundled files
 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,76 +54,95 @@ 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->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) {
-
-    # Not satisfiable
-    return $res->code(416) unless $size && $range =~ m/^bytes=(\d+)?-(\d+)?/;
-    $start = $1 if defined $1;
-    $end = $2 if defined $2 && $2 <= $end;
-    return $res->code(416) if $start > $end || $end > ($size - 1);
-
-    # Satisfiable
-    $res->code(206)->headers->content_length($end - $start + 1)
-      ->content_range("bytes $start-$end/$size");
-  }
-
+  return $res->content->asset($asset)
+    unless my $range = $c->req->headers->range;
+
+  # Not satisfiable
+  return $res->code(416) unless my $size = $asset->size;
+  return $res->code(416) unless $range =~ m/^bytes=(\d+)?-(\d+)?/;
+  my ($start, $end) = ($1 // 0, defined $2 && $2 < $size ? $2 : $size - 1);
+  return $res->code(416) if $start > $end;
+
+  # Satisfiable
+  $res->code(206)->headers->content_length($end - $start + 1)
+    ->content_range("bytes $start-$end/$size");
   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+$/;
+  # Protect files without extensions and templates with two extensions
+  return undef if $rel !~ /\.\w+$/ || $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);
 }
 
 sub _get_file {
   my ($self, $path) = @_;
   no warnings 'newline';
-  return -f $path && -r $path ? Mojo::Asset::File->new(path => $path) : undef;
+  return -f $path && -r _ ? 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;
@@ -134,13 +158,13 @@ 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
 
-L<Mojolicious::Static> is a static file server with C<Range> and
-C<If-Modified-Since> support based on
+L<Mojolicious::Static> is a static file server with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support based on
 L<RFC 7232|http://tools.ietf.org/html/rfc7232> and
 L<RFC 7233|http://tools.ietf.org/html/rfc7233>.
 
@@ -186,11 +210,38 @@ Serve static file for L<Mojolicious::Controller> object.
   my $asset = $static->file('../lib/MyApp.pm');
 
 Build L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object for a file,
-relative to L</"paths"> or from L</"classes">. Note that this method does not
-protect from traversing to parent directories.
+relative to L</"paths"> or from L</"classes">, or return C<undef> if it
+doesn't exist. Note that this method does not 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');
@@ -203,8 +254,8 @@ that this method does not protect from traversing to parent directories.
 
   $static->serve_asset(Mojolicious::Controller->new, Mojo::Asset::File->new);
 
-Serve a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object with C<Range>
-and C<If-Modified-Since> support.
+Serve a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support.
 
 =head1 SEE ALSO
 
@@ -1,8 +1,8 @@
 package Mojolicious::Validator::Validation;
 use Mojo::Base -base;
 
-use Carp 'croak';
-use Scalar::Util 'blessed';
+use Carp         ();
+use Scalar::Util ();
 
 has [qw(csrf_token topic validator)];
 has [qw(input output)] => sub { {} };
@@ -10,11 +10,11 @@ has [qw(input output)] => sub { {} };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
-  croak qq{Can't locate object method "$method" via package "$package"}
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
     unless $self->validator->checks->{$method};
   return $self->check($method => @_);
 }
@@ -54,6 +54,8 @@ sub error {
   return $self;
 }
 
+sub every_param { shift->_param(@_) }
+
 sub has_data { !!keys %{shift->input} }
 
 sub has_error { $_[1] ? exists $_[0]{error}{$_[1]} : !!keys %{$_[0]{error}} }
@@ -75,14 +77,12 @@ sub param {
   my ($self, $name) = @_;
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
   # List names
   return sort keys %{$self->output} unless defined $name;
 
-  my $value = $self->output->{$name};
-  my @values = ref $value eq 'ARRAY' ? @$value : ($value);
-  return wantarray ? @values : $values[0];
+  return $self->_param($name)->[-1];
 }
 
 sub required {
@@ -91,6 +91,11 @@ sub required {
   return $self->error($name => ['required']);
 }
 
+sub _param {
+  return [] unless defined(my $value = shift->output->{shift()});
+  return [ref $value eq 'ARRAY' ? @$value : $value];
+}
+
 1;
 
 =encoding utf8
@@ -122,8 +127,8 @@ L<Mojolicious::Validator::Validation> implements the following attributes.
 
 =head2 csrf_token
 
-  my $token   = $validation->token;
-  $validation = $validation->token('fa6a08...');
+  my $token   = $validation->csrf_token;
+  $validation = $validation->csrf_token('fa6a08...');
 
 CSRF token.
 
@@ -184,6 +189,16 @@ only be one per field.
 
   my ($check, $result, @args) = @{$validation->error('foo')};
 
+=head2 every_param
+
+  my $values = $validation->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $validation->every_param('foo')->[0];
+
 =head2 has_data
 
   my $bool = $validation->has_data;
@@ -214,11 +229,12 @@ Change validation L</"topic">.
 =head2 param
 
   my @names       = $validation->param;
-  my $foo         = $validation->param('foo');
-  my @foo         = $validation->param('foo');
+  my $value       = $validation->param('foo');
   my ($foo, $bar) = $validation->param(['foo', 'bar']);
 
-Access validated parameters, similar to L<Mojolicious::Controller/"param">.
+Access validated parameters. If there are multiple values sharing the same
+name, and you want to access more than just the last one, you can use
+L</"every_param">.
 
 =head2 required
 
@@ -233,10 +249,14 @@ In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above, you can also call
 validation checks provided by L</"validator"> on
 L<Mojolicious::Validator::Validation> objects, similar to L</"check">.
 
+  # Call validation checks
   $validation->required('foo')->size(2, 5)->like(qr/^[A-Z]/);
   $validation->optional('bar')->equal_to('foo');
   $validation->optional('baz')->in(qw(test 123));
 
+  # Longer version
+  $validation->required('foo')->check('size', 2,5)->check('like', qr/^[A-Z]/);
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -117,7 +117,7 @@
     %= include inline => app->renderer->_bundled('mojobar')
     <script>
       function mojoDrawer (handle, drawer) {
-        $(handle).click(function() {
+        $(handle).click(function () {
           $(drawer).slideToggle('slow');
           var text = $(handle + ' div.tap').text();
           text = text == "tap for more" ? "tap for less" : "tap for more";
@@ -125,7 +125,7 @@
         });
         $(drawer).toggle();
       }
-      $(document).ready(function() {
+      $(document).ready(function () {
         mojoDrawer('#trace', '#frames');
         mojoDrawer('#more', '#infos');
       });
@@ -179,8 +179,8 @@
             <div class="tap">tap for more</div>
             <script>
               var current = '#context';
-              $('#showcase').click(function() {
-                $(current).slideToggle('slow', function() {
+              $('#showcase').click(function () {
+                $(current).slideToggle('slow', function () {
                   if (current == '#context') {
                     current = '#insight';
                   }
@@ -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>
@@ -56,11 +56,11 @@
     %= link_to Documentation => 'http://mojolicio.us/perldoc'
     %= link_to Wiki => 'https://github.com/kraih/mojo/wiki'
     %= link_to GitHub => 'https://github.com/kraih/mojo'
-    %= link_to CPAN => 'http://metacpan.org/release/Mojolicious/'
-    %= link_to MailingList => 'http://groups.google.com/group/mojolicious'
+    %= link_to CPAN => 'https://metacpan.org/release/Mojolicious/'
+    %= link_to MailingList => 'https://groups.google.com/group/mojolicious'
     %= link_to Blog => 'http://blog.kraih.com'
-    %= link_to Twitter => 'http://twitter.com/kraih'
-    %= form_for 'http://google.com/cse' => (target => '_blank') => begin
+    %= link_to Twitter => 'https://twitter.com/kraih'
+    %= form_for 'https://www.google.com/cse' => (target => '_blank') => begin
       %= hidden_field cx => '014527573091551588235:pwfplkjpgbi'
       %= hidden_field ie => 'UTF-8'
       %= search_field 'q', placeholder => 'Search'
@@ -106,7 +106,7 @@
       }
     });
   });
-  $(document).ready(function() {
+  $(document).ready(function () {
     $('a[href^="#"]').addClass('mojoscroll');
     $(".mojoscroll").click(function(e) {
       e.preventDefault();
@@ -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;
@@ -2,9 +2,9 @@ package Mojolicious;
 use Mojo::Base 'Mojo';
 
 # "Fry: Shut up and take my money!"
-use Carp 'croak';
+use Carp ();
 use Mojo::Exception;
-use Mojo::Util 'decamelize';
+use Mojo::Util;
 use Mojolicious::Commands;
 use Mojolicious::Controller;
 use Mojolicious::Plugins;
@@ -14,17 +14,17 @@ use Mojolicious::Sessions;
 use Mojolicious::Static;
 use Mojolicious::Types;
 use Mojolicious::Validator;
-use Scalar::Util qw(blessed weaken);
-use Time::HiRes 'gettimeofday';
+use Scalar::Util ();
+use Time::HiRes  ();
 
 has commands => sub {
   my $commands = Mojolicious::Commands->new(app => shift);
-  weaken $commands->{app};
+  Scalar::Util::weaken $commands->{app};
   return $commands;
 };
 has controller_class => 'Mojolicious::Controller';
 has mode => sub { $ENV{MOJO_MODE} || $ENV{PLACK_ENV} || 'development' };
-has moniker  => sub { decamelize ref shift };
+has moniker  => sub { Mojo::Util::decamelize ref shift };
 has plugins  => sub { Mojolicious::Plugins->new };
 has renderer => sub { Mojolicious::Renderer->new };
 has routes   => sub { Mojolicious::Routes->new };
@@ -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.70';
 
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::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};
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
+    unless my $helper = $self->renderer->get_helper($method);
   return $self->build_controller->$helper(@_);
 }
 
@@ -68,10 +68,11 @@ 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};
+  Scalar::Util::weaken $c->{app};
 
   return $c;
 }
@@ -83,18 +84,15 @@ sub build_tx {
   return $tx;
 }
 
-sub defaults { shift->_dict(defaults => @_) }
+sub defaults { Mojo::Util::_stash(defaults => @_) }
 
 sub dispatch {
   my ($self, $c) = @_;
 
-  # Prepare transaction
-  my $tx = $c->tx;
-  $tx->res->code(undef) if $tx->is_websocket;
-  $self->sessions->load($c);
   my $plugins = $self->plugins->emit_hook(before_dispatch => $c);
 
   # Try to find a static file
+  my $tx = $c->tx;
   $self->static->dispatch($c) and $plugins->emit_hook(after_static => $c)
     unless $tx->res->code;
 
@@ -105,16 +103,13 @@ sub dispatch {
     my $method = $req->method;
     my $path   = $req->url->path->to_abs_string;
     $self->log->debug(qq{$method "$path".});
-    $stash->{'mojo.started'} = [gettimeofday];
+    $stash->{'mojo.started'} = [Time::HiRes::gettimeofday];
   }
 
   # Routes
   $plugins->emit_hook(before_routes => $c);
-  my $res = $tx->res;
-  return if $res->code;
-  if (my $code = ($tx->req->error // {})->{advice}) { $res->code($code) }
-  elsif ($tx->is_websocket) { $res->code(426) }
-  $c->render_not_found unless $self->routes->dispatch($c) || $tx->res->code;
+  $c->render_not_found
+    unless $tx->res->code || $self->routes->dispatch($c) || $tx->res->code;
 }
 
 sub handler {
@@ -128,7 +123,7 @@ sub handler {
 
   # Process with chain
   my $c = $self->build_controller(@_);
-  weaken $c->{tx};
+  Scalar::Util::weaken $c->{tx};
   $self->plugins->emit_chain(around_dispatch => $c);
 
   # Delayed response
@@ -153,20 +148,21 @@ 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));
+  # Hide controller attributes/methods
+  $r->hide(qw(app continue cookie every_cookie every_param));
+  $r->hide(qw(every_signed_cookie finish flash helpers 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(render_not_found render_to_string rendered req res respond_to));
+  $r->hide(qw(send session signed_cookie stash tx url_for validation write));
+  $r->hide(qw(write_chunk));
 
-  # Check if we have a log directory
+  # Check if we have a log directory that is writable
   my $mode = $self->mode;
   $self->log->path($home->rel_file("log/$mode.log"))
-    if -w $home->rel_file('log');
+    if -d $home->rel_file('log') && -w _;
 
   $self->plugin($_)
     for qw(HeaderCondition DefaultHelpers TagHelpers EPLRenderer EPRenderer);
@@ -187,7 +183,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 +219,7 @@ Mojolicious - Real-time web framework
   }
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -248,7 +248,8 @@ parsed.
 
 This is a very powerful hook and should not be used lightly, it makes some
 rather advanced features such as upload progress bars possible. Note that this
-hook will not work for embedded applications. (Passed the transaction and
+hook will not work for embedded applications, because only the host
+application gets to build transactions. (Passed the transaction and
 application object)
 
 =head2 before_dispatch
@@ -356,8 +357,9 @@ Useful for rewriting outgoing responses and other post-processing tasks.
 Emitted right before the L</"before_dispatch"> hook and wraps around the whole
 dispatch process, so you have to manually forward to the next hook if you want
 to continue the chain. Default exception handling with
-L<Mojolicious::Controller/"render_exception"> is the first hook in the chain
-and a call to L</"dispatch"> the last, yours will be in between.
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception"> is the first
+hook in the chain and a call to L</"dispatch"> the last, yours will be in
+between.
 
   $app->hook(around_dispatch => sub {
     my ($next, $c) = @_;
@@ -456,7 +458,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
 
@@ -664,7 +666,11 @@ Note that application helpers are always called with a new default controller
 object, so they can't depend on or change controller state, which includes
 request, response and stash.
 
-  $app->log->debug($app->dumper({foo => 'bar'}));
+  # Call helper
+  say $app->dumper({foo => 'bar'});
+
+  # Longer version
+  say $app->build_controller->helpers->dumper({foo => 'bar'});
 
 =head1 BUNDLED FILES
 
@@ -737,6 +743,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 +861,6 @@ Ilya Chesnokov
 
 James Duncan
 
-Jan Henning Thorsen
-
 Jan Jona Javorsek
 
 Jan Schmidt
@@ -877,6 +883,8 @@ Kevin Old
 
 Kitamura Akatsuki
 
+Klaus S. Madsen
+
 Lars Balker Rasmussen
 
 Leon Brocard
@@ -885,6 +893,10 @@ Magnus Holm
 
 Maik Fischer
 
+Mark Fowler
+
+Mark Grimes
+
 Mark Stosberg
 
 Marty Tennison
@@ -929,6 +941,8 @@ Peter Edwards
 
 Pierre-Yves Ritschard
 
+Piotr Roszatycki
+
 Quentin Carbonneaux
 
 Rafal Pocztarski
@@ -969,6 +983,8 @@ Tatsuhiko Miyagawa
 
 Terrence Brannon
 
+Tianon Gravi
+
 Tomas Znamenacek
 
 Ulrich Habel
@@ -97,7 +97,7 @@ sub element_exists_not {
 
 sub finish_ok {
   my $self = shift;
-  $self->tx->finish(@_);
+  $self->tx->finish(@_) if $self->tx->is_websocket;
   Mojo::IOLoop->one_tick while !$self->{finished};
   return $self->_test('ok', 1, 'closed WebSocket');
 }
@@ -258,21 +258,24 @@ sub reset_session {
 
 sub send_ok {
   my ($self, $msg, $desc) = @_;
+
+  $desc ||= 'send message';
+  return $self->_test('ok', 0, $desc) unless $self->tx->is_websocket;
+
   $self->tx->send($msg => sub { Mojo::IOLoop->stop });
   Mojo::IOLoop->start;
-  return $self->_test('ok', 1, $desc || 'send message');
+  return $self->_test('ok', 1, $desc);
 }
 
 sub status_is {
   my ($self, $status, $desc) = @_;
-  $desc ||= "$status " . $self->tx->res->new(code => $status)->default_message;
+  $desc ||= "$status " . $self->tx->res->default_message($status);
   return $self->_test('is', $self->tx->res->code, $status, $desc);
 }
 
 sub status_isnt {
   my ($self, $status, $desc) = @_;
-  $desc
-    ||= "not $status " . $self->tx->res->new(code => $status)->default_message;
+  $desc ||= "not $status " . $self->tx->res->default_message($status);
   return $self->_test('isnt', $self->tx->res->code, $status, $desc);
 }
 
@@ -329,7 +332,7 @@ sub _message {
   }
 
   # Decode text frame if there is no type check
-  else { $msg = decode 'UTF-8', $msg if $type eq 'text' }
+  else { $msg = decode 'UTF-8', $msg if ($type // '') eq 'text' }
 
   return $self->_test($name, $msg // '', $value, $desc);
 }
@@ -345,7 +348,7 @@ sub _request_ok {
     $self->ua->start(
       $tx => sub {
         my ($ua, $tx) = @_;
-        $self->tx($tx);
+        $self->{finished} = [] unless $self->tx($tx)->tx->is_websocket;
         $tx->on(finish => sub { shift; $self->{finished} = [@_] });
         $tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] });
         $tx->on(text   => sub { push @{$self->{messages}}, [text   => pop] });
@@ -354,7 +357,7 @@ sub _request_ok {
     );
     Mojo::IOLoop->start;
 
-    my $desc = encode 'UTF-8', "WebSocket $url";
+    my $desc = encode 'UTF-8', "WebSocket handshake with $url";
     return $self->_test('ok', $self->tx->is_websocket, $desc);
   }
 
@@ -421,8 +424,17 @@ Test::Mojo - Testing Mojo!
 
 =head1 DESCRIPTION
 
-L<Test::Mojo> is a collection of testing helpers for everyone developing
-L<Mojo> and L<Mojolicious> applications.
+L<Test::Mojo> is a test user agent based on L<Mojo::UserAgent>, it is usually
+used together with L<Test::More> to test L<Mojolicious> applications. Just run
+your tests with the command L<Mojolicious::Command::test> or L<prove>.
+
+  $ ./script/my_app test
+  $ ./script/my_app test -v t/foo.t
+  $ prove -l -v t/foo.t
+
+If it is not already defined, the C<MOJO_LOG_LEVEL> environment variable will
+be set to C<debug> or C<fatal>, depending on the value of the
+C<HARNESS_IS_VERBOSE> environment variable.
 
 =head1 ATTRIBUTES
 
@@ -433,7 +445,14 @@ L<Test::Mojo> implements the following attributes.
   my $msg = $t->message;
   $t      = $t->message([text => $bytes]);
 
-Current WebSocket message.
+Current WebSocket message represented as an array reference containing the
+frame type and payload.
+
+  # More specific tests
+  use Mojo::JSON 'decode_json';
+  my $hash = decode_json $t->message->[1];
+  is ref $hash, 'HASH', 'right reference';
+  is $hash->{foo}, 'bar', 'right value';
 
   # Test custom message
   $t->message([binary => $bytes])
@@ -465,7 +484,8 @@ True if the last test was successful.
   my $tx = $t->tx;
   $t     = $t->tx(Mojo::Transaction::HTTP->new);
 
-Current transaction, usually a L<Mojo::Transaction::HTTP> object.
+Current transaction, usually a L<Mojo::Transaction::HTTP> or
+L<Mojo::Transaction::WebSocket> object.
 
   # More specific tests
   is $t->tx->res->json->{foo}, 'bar', 'right value';
@@ -504,7 +524,7 @@ following new ones.
 =head2 app
 
   my $app = $t->app;
-  $t      = $t->app(MyApp->new);
+  $t      = $t->app(Mojolicious->new);
 
 Access application with L<Mojo::UserAgent::Server/"app">.
 
@@ -591,9 +611,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 +650,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.
@@ -640,41 +660,45 @@ arguments as L<Mojo::UserAgent/"get">, except for the callback.
   # Run tests against remote host
   $t->get_ok('http://mojolicio.us/perldoc')->status_is(200);
 
+  # Run additional tests on the transaction
+  $t->get_ok('/foo')->status_is(200);
+  is $t->tx->res->dom->at('input')->{value}, 'whatever', 'right value';
+
 =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.
 
 =head2 header_is
 
-  $t = $t->header_is(Expect => 'fun');
-  $t = $t->header_is(Expect => 'fun', 'right header');
+  $t = $t->header_is(ETag => '"abc321"');
+  $t = $t->header_is(ETag => '"abc321"', 'right header');
 
 Check response header for exact match.
 
 =head2 header_isnt
 
-  $t = $t->header_isnt(Expect => 'fun');
-  $t = $t->header_isnt(Expect => 'fun', 'different header');
+  $t = $t->header_isnt(Etag => '"abc321"');
+  $t = $t->header_isnt(ETag => '"abc321"', 'different header');
 
 Opposite of L</"header_is">.
 
 =head2 header_like
 
-  $t = $t->header_like(Expect => qr/fun/);
-  $t = $t->header_like(Expect => qr/fun/, 'right header');
+  $t = $t->header_like(ETag => qr/abc/);
+  $t = $t->header_like(ETag => qr/abc/, 'right header');
 
 Check response header for similar match.
 
 =head2 header_unlike
 
-  $t = $t->header_like(Expect => qr/fun/);
-  $t = $t->header_like(Expect => qr/fun/, 'different header');
+  $t = $t->header_unlike(ETag => qr/abc/);
+  $t = $t->header_unlike(ETag => qr/abc/, 'different header');
 
 Opposite of L</"header_like">.
 
@@ -818,9 +842,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 +862,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 +872,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 +891,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.
@@ -966,7 +990,7 @@ Open a WebSocket connection with transparent handshake, takes the same
 arguments as L<Mojo::UserAgent/"websocket">, except for the callback.
 
   # WebSocket with permessage-deflate compression
-  $t->websocket('/x' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
+  $t->websocket_ok('/' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
     ->send_ok('y' x 50000)
     ->message_ok
     ->message_is('z' x 50000)
@@ -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,
@@ -43,8 +45,7 @@ sub _request {
 
   my $tx  = $ua->start($ua->build_tx(@_));
   my $err = $tx->error;
-  warn
-    qq/Problem loading URL "@{[$tx->req->url->to_abs]}". ($err->{message})\n/
+  warn qq/Problem loading URL "@{[$tx->req->url]}": $err->{message}\n/
     if $err && !$err->{code};
 
   return $tx->res;
@@ -79,6 +80,9 @@ Every L<ojo> one-liner is also a L<Mojolicious::Lite> application.
 
   $ perl -Mojo -E 'get "/" => {inline => "%= time"}; app->start' get /
 
+If it is not already defined, the C<MOJO_LOG_LEVEL> environment variable will
+be set to C<fatal>.
+
 =head1 FUNCTIONS
 
 L<ojo> implements the following functions, which are automatically exported.
@@ -111,7 +115,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,17 +123,17 @@ 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.
 
-  $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1, h2, h3")->text'
+  $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1")->map("text")->join("\n")'
 
 =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 +148,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 +169,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 +185,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 +193,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.
@@ -1,18 +1,15 @@
-#!/usr/bin/env perl
+#!perl
 
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case);
 
 GetOptions
-  'f|foreground' => sub { $ENV{HYPNOTOAD_FOREGROUND} = 1 },
+  'f|foreground' => \$ENV{HYPNOTOAD_FOREGROUND},
   'h|help'       => \my $help,
-  's|stop'       => sub { $ENV{HYPNOTOAD_STOP}       = 1 },
-  't|test'       => sub { $ENV{HYPNOTOAD_TEST}       = 1 };
+  's|stop'       => \$ENV{HYPNOTOAD_STOP},
+  't|test'       => \$ENV{HYPNOTOAD_TEST};
 
 my $app = shift || $ENV{HYPNOTOAD_APP};
 if ($help || !$app) {
@@ -33,9 +30,9 @@ hypnotoad - Hypnotoad HTTP and WebSocket server
 
   Usage: hypnotoad [OPTIONS] [APPLICATION]
 
-    hypnotoad script/myapp
-    hypnotoad myapp.pl
-    hypnotoad -f myapp.pl
+    hypnotoad ./script/my_app
+    hypnotoad ./myapp.pl
+    hypnotoad -f ./myapp.pl
 
   Options:
     -f, --foreground   Keep manager process in foreground.
@@ -1,11 +1,8 @@
-#!/usr/bin/env perl
+#!perl
 
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 require Mojolicious::Commands;
 Mojolicious::Commands->start_app('Mojo::HelloWorld');
 
@@ -1,18 +1,15 @@
-#!/usr/bin/env perl
+#!perl
 
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case);
 
 GetOptions
   'h|help'     => \my $help,
   'l|listen=s' => \my @listen,
-  'm|mode=s'   => sub { $ENV{MOJO_MODE} = $_[1] },
-  'v|verbose'  => sub { $ENV{MORBO_VERBOSE} = 1 },
+  'm|mode=s'   => \$ENV{MOJO_MODE},
+  'v|verbose'  => \$ENV{MORBO_VERBOSE},
   'w|watch=s'  => \my @watch;
 
 my $app = shift;
@@ -37,10 +34,11 @@ morbo - Morbo HTTP and WebSocket development server
 
   Usage: morbo [OPTIONS] [APPLICATION]
 
-    morbo script/myapp
-    morbo myapp.pl
-    morbo -m production -l https://*:443 -l http://[::]:3000 myapp.pl
-    morbo -w /usr/local/lib -w public myapp.pl
+    morbo ./script/my_app
+    morbo ./myapp.pl
+    morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl
+    morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl
+    morbo -w /usr/local/lib -w public ./myapp.pl
 
   Options:
     -h, --help                     Show this message.
@@ -9,10 +9,15 @@ use Mojo::Asset::Memory;
 
 # File asset
 my $file = Mojo::Asset::File->new;
+is $file->size, 0, 'file is empty';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
+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';
 is $file->contains('db'),  -1, 'does not contain "db"';
+is $file->size, 3, 'right size';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
 
 # Cleanup
 my $path = $file->path;
@@ -26,6 +31,11 @@ $mem->add_chunk('abc');
 is $mem->contains('abc'), 0,  '"abc" at position 0';
 is $mem->contains('bc'),  1,  '"bc" at position 1';
 is $mem->contains('db'),  -1, 'does not contain "db"';
+is $mem->size, 3, 'right size';
+ok $mem->mtime > (time - 100), 'right mtime';
+is $mem->mtime, Mojo::Asset::Memory->new->mtime, 'same mtime';
+my $mtime = $mem->mtime;
+is $mem->mtime($mtime + 23)->mtime, $mtime + 23, 'right mtime';
 
 # Empty file asset
 $file = Mojo::Asset::File->new;
@@ -183,18 +193,6 @@ ok !$asset->is_file, 'stored in memory';
 $asset = $asset->add_chunk('lala');
 ok !$asset->is_file, 'stored in memory';
 
-# Append to file asset
-$file = Mojo::Asset::File->new(cleanup => 0);
-is $file->add_chunk('hello')->slurp, 'hello', 'right content';
-$path = $file->path;
-undef $file;
-ok -e $path, 'file still exists';
-$file = Mojo::Asset::File->new(path => $path, cleanup => 1);
-is $file->add_chunk(' world')->slurp, 'hello world',  'right content';
-is $file->add_chunk('!')->slurp,      'hello world!', 'right content';
-undef $file;
-ok !-e $path, 'file has been cleaned up';
-
 # Temporary directory
 {
   my $tmpdir = tempdir CLEANUP => 1;
@@ -226,6 +224,7 @@ $file = Mojo::Asset::File->new(cleanup => 0)->add_chunk('test');
 ok $file->is_file, 'stored in file';
 is $file->slurp,   'test', 'right content';
 is $file->size,    4, 'right size';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
 is $file->contains('es'), 1, '"es" at position 1';
 $path = $file->path;
 undef $file;
@@ -13,6 +13,8 @@ use base 'Mojo::BaseTest::Base2';
 __PACKAGE__->attr(heads => 1);
 __PACKAGE__->attr('name');
 
+sub more_heads { shift->{heads} += shift // 1 }
+
 package Mojo::BaseTestTest;
 use Mojo::Base 'Mojo::BaseTest';
 
@@ -66,6 +68,10 @@ $monkey = Mojo::BaseTest->new;
 is $monkey->tap(sub { $_->name('foo') })->name, 'foo', 'right attribute value';
 is $monkey->tap(sub { shift->name('bar')->name })->name, 'bar',
   'right attribute value';
+is $monkey->tap('heads')->heads, 1, 'right attribute value';
+is $monkey->more_heads, 2, 'right attribute value';
+is $monkey->tap('more_heads')->heads, 3, 'right attribute value';
+is $monkey->tap(more_heads => 3)->heads, 6, 'right attribute value';
 
 # Inherit -base flag
 $monkey = Mojo::BaseTest::Base3->new(evil => 1);
@@ -1,7 +1,9 @@
 use Mojo::Base -strict;
 
 use Test::More;
-use File::Spec::Functions qw(catfile splitdir);
+use Cwd 'abs_path';
+use File::Basename 'dirname';
+use File::Spec::Functions 'catfile';
 use File::Temp 'tempdir';
 use FindBin;
 use Mojo::ByteStream 'b';
@@ -101,18 +103,17 @@ ok !ref $stream->to_string, 'nested bytestream stringified';
 
 # split
 $stream = b('1,2,3,4,5');
-is_deeply [$stream->split(',')->each],   [1, 2, 3, 4, 5], 'right elements';
-is_deeply [$stream->split(qr/,/)->each], [1, 2, 3, 4, 5], 'right elements';
-is_deeply [b('54321')->split('')->each], [5, 4, 3, 2, 1], 'right elements';
-is_deeply [b('')->split('')->each],    [], 'no elements';
-is_deeply [b('')->split(',')->each],   [], 'no elements';
-is_deeply [b('')->split(qr/,/)->each], [], 'no elements';
+is_deeply $stream->split(',')->to_array,   [1, 2, 3, 4, 5], 'right elements';
+is_deeply $stream->split(qr/,/)->to_array, [1, 2, 3, 4, 5], 'right elements';
+is_deeply b('54321')->split('')->to_array, [5, 4, 3, 2, 1], 'right elements';
+is_deeply b('')->split('')->to_array,    [], 'no elements';
+is_deeply b('')->split(',')->to_array,   [], 'no elements';
+is_deeply b('')->split(qr/,/)->to_array, [], 'no elements';
 $stream = b('1/2/3');
 is $stream->split('/')->map(sub { $_->quote })->join(', '), '"1", "2", "3"',
   'right result';
 is $stream->split('/')->map(sub { shift->quote })->join(', '),
   '"1", "2", "3"', 'right result';
-is $stream->split('/')->quote->join(', '), '"1", "2", "3"', 'right result';
 
 # length
 is b('foo bar baz')->size, 11, 'size is 11';
@@ -139,7 +140,7 @@ b('te', 'st')->say($handle);
 is $buffer, "test\n123\n\"123\"\n", 'right output';
 
 # slurp
-my $file = catfile splitdir($FindBin::Bin), qw(templates exception.mt);
+my $file = abs_path catfile(dirname(__FILE__), 'templates', 'exception.mt');
 $stream = b($file)->slurp;
 is $stream, "test\n% die;\n123\n", 'right content';
 $stream = b($file)->slurp->split("\n")->grep(qr/die/)->join;
@@ -151,8 +152,4 @@ $file = catfile $dir, 'test.txt';
 is b("just\nworks!")->spurt($file)->quote, qq{"just\nworks!"}, 'right result';
 is b($file)->slurp, "just\nworks!", 'successful roundtrip';
 
-# Boolean context
-ok !Mojo::ByteStream->new(0),  '"0" is falsy';
-ok !!Mojo::ByteStream->new(1), '"1" is truthy';
-
 done_testing();
@@ -25,8 +25,7 @@ is $cache->get('two'), 2,     'right result';
 
 $cache = Mojo::Cache->new(max_keys => 3);
 is $cache->get('foo'), undef, 'no result';
-$cache->set(foo => 'bar');
-is $cache->get('foo'), 'bar', 'right result';
+is $cache->set(foo => 'bar')->get('foo'), 'bar', 'right result';
 $cache->set(bar => 'baz');
 is $cache->get('foo'), 'bar', 'right result';
 is $cache->get('bar'), 'baz', 'right result';
@@ -40,4 +39,12 @@ is $cache->get('bar'),  'baz',  'right result';
 is $cache->get('baz'),  'yada', 'right result';
 is $cache->get('yada'), 23,     'right result';
 
+$cache = Mojo::Cache->new(max_keys => 0);
+is $cache->get('foo'), undef, 'no result';
+is $cache->set(foo => 'bar')->get('foo'), undef, 'no result';
+$cache = Mojo::Cache->new(max_keys => -1);
+is $cache->get('foo'), undef, 'no result';
+$cache->set(foo => 'bar');
+is $cache->get('foo'), undef, 'no result';
+
 done_testing();
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICMDCCAZmgAwIBAgIJAL3i8FjIa505MA0GCSqGSIb3DQEBBQUAMBsxCzAJBgNV
+BAYTAlVTMQwwCgYDVQQDEwNiYWQwHhcNMTQxMjEyMDUwNDA3WhcNMzQxMjA3MDUw
+NDA3WjAbMQswCQYDVQQGEwJVUzEMMAoGA1UEAxMDYmFkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDa3Y/PG8MEfLboJQ2pPKOTgXLCBDPPPii8L3AYMaqo6b9M
+S4ofj0qE6uiAne4ZMOoXl18U/9isZ5V8DKYTiNBDwTYyl9r6n+z+4uyINfxPhwhm
+q3eYK2/WRRNgGkaBX1Jn+woSQNj/Xq9hxu0oy5oUez+vDtg5vfgvQHZX48igbQID
+AQABo3wwejAdBgNVHQ4EFgQUG+9Mb16GLXdUYxAkv5T0VNIXAMgwSwYDVR0jBEQw
+QoAUG+9Mb16GLXdUYxAkv5T0VNIXAMihH6QdMBsxCzAJBgNVBAYTAlVTMQwwCgYD
+VQQDEwNiYWSCCQC94vBYyGudOTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
+A4GBALxX+nkjDBh+Ir6QqBcdhiZ8i4rBhLUHaNDUFKq18WoxpTOtgwh7LUf+gT3D
+XeelteXWgd4Ta7+emaAMS0+lSceJGHJJGYqcgHecIrKWtBAXL3z5K1YTZJb6jpCb
+nAdXvj3b84/6AkrguWUeh/S0VMUtI11L984xCPbNizvnepAV
+-----END CERTIFICATE-----
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDa3Y/PG8MEfLboJQ2pPKOTgXLCBDPPPii8L3AYMaqo6b9MS4of
+j0qE6uiAne4ZMOoXl18U/9isZ5V8DKYTiNBDwTYyl9r6n+z+4uyINfxPhwhmq3eY
+K2/WRRNgGkaBX1Jn+woSQNj/Xq9hxu0oy5oUez+vDtg5vfgvQHZX48igbQIDAQAB
+AoGAJuHz2YyKEfMCH63thmJygRMfSx6qAah+XihjVrkEI6wbQzesWzz+tSxyj97+
+3NTW0cgfqUBmwwJICVjJ7HCC5yJQMvgAVv8MdPjuabZOEVthhHJJKe9CtVlkQC+F
+5gybqf7m5EIWosd9ROjhBes4FaPLDG3ujOO27KEzY9CJmpUCQQD9aAn8qe7TVXnj
+on9q6lu5aNB1lQ1G2h1yOkZDauY1TDec8/Vk6MjLmWIKu0GAATQAnGohNPGxVwZP
+WdchXbAHAkEA3RsFRJkigPHudmimE2RH5Tf8UxfE89R7lhsBs7hTXSxeLUx2U5iv
+G2g5KcKKnOJ+di5hB1dzW9q8zgTYGGEm6wJBAJqNcMHkVuAYGt5GRpZL16OulK2S
+OeXDs//uINqPgVZyZNzaQgnInGbo5s4KxXkvwqq4u1YDd4GkxRlyfu4/uNMCQEST
+KtEZxY/EeTcUQd3lzj6TXJjQ2G7fSe1GNwLsej59kj/uZQyMYFLZwlDnW2JJBPvW
+apWCU+77Km+jJPp6tz0CQQDleMOZi70ikb04E0kagpJiRtNOLThDKjY6r6ZZWPro
+saqxw29J/bAReUYXAcW5EzwRG97R/2u1HY77Y43YA3v0
+-----END RSA PRIVATE KEY-----
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICRDCCAa2gAwIBAgIJANJ9pLjf51nuMA0GCSqGSIb3DQEBBQUAMCExCzAJBgNV
-BAYTAlVTMRIwEAYDVQQDEwliYWRjbGllbnQwHhcNMTIwNDE4MTczODE5WhcNMzIw
-NDEzMTczODE5WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAxMJYmFkY2xpZW50MIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5eH1UjDFvU8qrQCNLPdiznaBxm3A2
-NO+XfuEf4QW4t98Sr0cvw0VI8MdKl+LC9VYo3L4Wu7hYTvG+PZlBAK7h2lSUl2CF
-oGkWUtX8K2Ae9SLXO35RISDhuMsPDg3YtBuLrn1ionPCSrPCJg8h4kNj23v7S8/T
-y1Plw1iV+kKffwIDAQABo4GDMIGAMB0GA1UdDgQWBBSgAgZGx1VOLW4Emr3MJ4yc
-QjvEJzBRBgNVHSMESjBIgBSgAgZGx1VOLW4Emr3MJ4ycQjvEJ6ElpCMwITELMAkG
-A1UEBhMCVVMxEjAQBgNVBAMTCWJhZGNsaWVudIIJANJ9pLjf51nuMAwGA1UdEwQF
-MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEATtl9MkWPg38MnkSyAe9qbXISEBAQLj2h
-NX6jNDENR/VVmVt0/JcwJ4HhiHrEH5BmWBNgJCwxytQK/UjUI5cBAfMeoUe47+2v
-fYo63TiYt/PHN/kPKquy6iC/8AVWzGTINgmmT/HlUf2SgcWYUeGvQqOrBr9DpFkx
-pooAOS8N6Lk=
------END CERTIFICATE-----
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQC5eH1UjDFvU8qrQCNLPdiznaBxm3A2NO+XfuEf4QW4t98Sr0cv
-w0VI8MdKl+LC9VYo3L4Wu7hYTvG+PZlBAK7h2lSUl2CFoGkWUtX8K2Ae9SLXO35R
-ISDhuMsPDg3YtBuLrn1ionPCSrPCJg8h4kNj23v7S8/Ty1Plw1iV+kKffwIDAQAB
-AoGAaDCXt3dKDhA0Oh+9s/b6VWynRSrczpbOQXy1WwM+A5bHaeetRdechG0+h1jr
-ujm4PRxEbODnnjQ6+XQBM9L+akv4FS0+57YZbAwjHk0vm6G9x0J8bQqtZLYbs/tW
-M4K/eVo0eWzuiANnuAXYP/Ijp/vcFCWYYHHKumh/e2YNygECQQDxKda25YGebNg1
-NB8oymY5PjovL8/0xbIQABwYwZvY4vIL9bITgwLj/TERGeh+En2/wzURH48VsI1K
-pF7q0IpvAkEAxOGG/ZnpDIv4bkx1GEtXYCcU+uzTOxSndSrIE2CbByV9wOsEGhD6
-Pf7Nu7qNUUCgdyVcMM+Zv+g9TXfGpz8D8QJBAIL0CB7Ko0bmrcS+J8lbloSw8KFc
-4hx1GYqstJUQ1NLB8VNjOC52LqSkgPdbTS7gVZ5CntdMfsL1WtPUS6JQWUsCQBc4
-yqVK7BQQxmKsHfuZl1q+AVl/aYLEcuP9AdBXSrHTGDwS/E7excVeMoH8BQMablLf
-UzpmoiniiKiEgifj5OECQGmMaRERgu9wG1Kd+lwdFVSLZ8WSxq/Oee9ZXO1N9o0x
-FrRypqdB8w3ETUU1TAUJbaE2ShgrJPT50emi3dE9Vo8=
------END RSA PRIVATE KEY-----
@@ -1,14 +1,14 @@
 -----BEGIN CERTIFICATE-----
-MIICLTCCAZagAwIBAgIJAIXFBqtx/DgwMA0GCSqGSIb3DQEBBQUAMBoxCzAJBgNV
-BAYTAlVTMQswCQYDVQQDEwJjYTAeFw0xMjA0MTgxNzM3MDFaFw0zMjA0MTMxNzM3
+MIICLTCCAZagAwIBAgIJALx7JaBkhvU1MA0GCSqGSIb3DQEBBQUAMBoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQDEwJjYTAeFw0xNDEyMTIwNTAzMDFaFw0zNDEyMDcwNTAz
 MDFaMBoxCzAJBgNVBAYTAlVTMQswCQYDVQQDEwJjYTCBnzANBgkqhkiG9w0BAQEF
-AAOBjQAwgYkCgYEAsN5A5sqKSknGZkPFp0Y4+DHXIxbyc8nyjgQg7RVrO2x1x0PJ
-5l6Kxa4JV012zzZ5G75ARpsZnZ9iUpS23NkcTf+ZDKmGp6iARsGnOHg3VQVvh8ay
-Qr8Qe48AmWoYmRuaDasvrPCGPwxtWtwCO7qCN9QhiovXLGSGzLpzHGhXgQECAwEA
-AaN7MHkwHQYDVR0OBBYEFCHumkco9aRJCbx2U7HC/SI089upMEoGA1UdIwRDMEGA
-FCHumkco9aRJCbx2U7HC/SI089upoR6kHDAaMQswCQYDVQQGEwJVUzELMAkGA1UE
-AxMCY2GCCQCFxQarcfw4MDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
-AGB4qLzmfgjKfmA4vNlyXxYLgGJt0DmnzSXmsAE9h3eVj7ofidf6PnTiehMHJdVo
-wCtMKCv4wT5GFd3TBPu9ZJw9QpyMMuDcOPVgAHg1PieZt4rYQYO5q2Zex6SCcbNx
-DKKlg/x/eDFsMDERI+qsHIhzSkkTMuzrK8A09gaXlLIO
+AAOBjQAwgYkCgYEAozbnJr3IvXwQeQV7n63p8XgXSpLgGmOsldyyIOBNy9zhoN7e
+ZFvzNysr80+4a2+o3vRzXptGYuFjocBRruWm7Z42HLOKiiI/X317whfvm4dqsa9s
+Bo4WN+2Ptsz31X+v/XnD6oZS5nLCeQ/WNuzd6Am9V0Y6Z//NReGOA3lotPkCAwEA
+AaN7MHkwHQYDVR0OBBYEFPW1qzZDKPG4QYd7GN8rHWpVCdaLMEoGA1UdIwRDMEGA
+FPW1qzZDKPG4QYd7GN8rHWpVCdaLoR6kHDAaMQswCQYDVQQGEwJVUzELMAkGA1UE
+AxMCY2GCCQC8eyWgZIb1NTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
+ABHz4htmRT6rG6tcSZ4dfpFZpd0Njj2pikkeymDK6s7vmfKVdY7NK0kisoHG88ms
+y2mzRwbUSshuYfjghqZdizwf5bdprNSHRPfeey44pNTV8aVeKK13IxHGZk49hWHX
+cq59GdoKy+AX9n0NI58u2GUrdwmM7Q8Qo8gVvfY19NFW
 -----END CERTIFICATE-----
@@ -1,15 +1,15 @@
 -----BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQCw3kDmyopKScZmQ8WnRjj4MdcjFvJzyfKOBCDtFWs7bHXHQ8nm
-XorFrglXTXbPNnkbvkBGmxmdn2JSlLbc2RxN/5kMqYanqIBGwac4eDdVBW+HxrJC
-vxB7jwCZahiZG5oNqy+s8IY/DG1a3AI7uoI31CGKi9csZIbMunMcaFeBAQIDAQAB
-AoGAIkYvlYnSWqsJQ3AGsW+uQoqGAkrEGSQdWN/78XWhHhbajA7a5UWU5tHfBYgB
-ag5IUdQAaK5NvHnSd+ZRuWJpVXFxrXlPcRdhnFHXSxqpjjL7ia62kKdv0goSxBul
-dHwBV2NINZU0VfBGiF/d2uUQa8FCu73Ku0qDwad+h22NuQECQQDkVCEtmzyI0bIH
-6wutgVT6EQ4WRFRJwQniFQ2qAXfHLrtORnunV0DIi/fUu4HXb/VHPDjjgEsi/gJd
-lsJjH6ppAkEAxk2RrsdUFlrkpA2sOnNh6xUCEdNLyY8cp0KAad2Oy7b2/yV8fC8C
-r/7iYBXjrNI5KdUC88JWd361VVvExdHe2QJAIW27ycyPiUyCLyyXchicU1xJ3rK0
-UHQFuWbxsDE00yGhZK1nDzH0Fyh+6VNmiB8pHvvPLGetGHtt5zvaS97n+QJBAL5X
-knCLT8Etea2mmhx4yBJX1ea6RFKyFbTOdmgnjHspoigCGV0x2oiIRU4pdKJcHxZy
-KiVD8q88Zl4KefPPoPECQGjoVv3O8QPN+hPdiBwgNK/QU7os7itDQRguzlK7iPQK
-e0k4mA+XCo3VX2lFgpKrYwcBV16n0K3jvN6ELORGMaM=
+MIICXQIBAAKBgQCjNucmvci9fBB5BXufrenxeBdKkuAaY6yV3LIg4E3L3OGg3t5k
+W/M3KyvzT7hrb6je9HNem0Zi4WOhwFGu5abtnjYcs4qKIj9ffXvCF++bh2qxr2wG
+jhY37Y+2zPfVf6/9ecPqhlLmcsJ5D9Y27N3oCb1XRjpn/81F4Y4DeWi0+QIDAQAB
+AoGAD3Sdgv+pyLe835jzUfWoF6OloPwzmIwjxbU2cQKMm2d5PkCckqhaabj+2nWM
+IzBZr3M6kZTmn2p0gtaxZXx9yA/iSc/H5agWtmP2L5+XLVFY2U+GlLtlF8BPCzf8
+GoUHfq4phHyJ0tqzduVxO6p9UgdLhud0E07KuiF/lw0a5uECQQDOSIoMezNkOKkt
+Ilza9RZR4R2S1NB23RfASddKyQK91sXC7uBiKvQO+GWzlJnbpTOEjpxTi0zMaYBS
+9OpODiCVAkEAyo0POddC8VOVSSXZfbVuATmbr7Gu9JR9Qy9zzXObrrUtQDAX/8Kp
+MKAujFlT+6XqM/AFfP9ZH4FvFj2qLVX11QJAX/hLkYb339akjoUAIjYIDkvnUFqG
+KeaumB1Cdl6SUfPLyecMqV9GcHiMCEJIWnG/SBp5DD0wm6ExvGaJY4sbCQJBAJNE
+Bfr24GwXaiX5x+yXGbj4SpJuLJcU0xIjER4iXOGkRpcxoHFK9bot7EoMtHv1gJds
+foEIIqM+dycDhJRbuFUCQQCRnWBGk+Ess3FIilnfS0wfHzuY0DvMgyiF+qu+EWuD
+254B3GxDGf2PMDa59DSY+zIzj/vfIhQqYdjiJbJpoXGK
 -----END RSA PRIVATE KEY-----
@@ -1,12 +1,12 @@
 -----BEGIN CERTIFICATE-----
-MIIBsjCCARsCCQCiVUE8+nXmezANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV
-UzELMAkGA1UEAxMCY2EwHhcNMTIwNDE4MTczNzQ4WhcNMzIwNDEzMTczNzQ4WjAh
-MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQCwIlSufhfNTPhuyO+ZMM6aR090jl9pvAhZen1RL+2heDQg
-XXu1MXVMmMNKGkjeVnJdOPgM/TyC11FBUmc7LWApdGduHz18v5b1jM2HxIgzYWnJ
-WEDUEwVbgTHQgyikcJ1hJNSs1oXSqJpamrWKZaBMApyzbfNIgvOBJyhvqTMt5QID
-AQABMA0GCSqGSIb3DQEBBQUAA4GBAAaQZ7GyKPC4OcRMguTGgrv3xOQyz+TB4KVa
-Q/yQfa4kMAXHfULm+DOYQy+qmyEJA+N/ddBJSlnO0q3O6gSMZeyJzO60gQr4Kals
-dLaqwpw56zD4/DtJy5QQ4tu0YOFPqGJUvPK9Rxeo/9HYbuBAFK8eRS1UB3mpVt/Z
-vzuekxuG
+MIIBsjCCARsCCQCptEBZlSnk3zANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV
+UzELMAkGA1UEAxMCY2EwHhcNMTQxMjEyMDUwMzQ5WhcNMzQxMjA3MDUwMzQ5WjAh
+MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJMTI3LjAuMC4xMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC74ZFnrU3GHG/XdPih2bdMLXKlKL86dTsdt5y4dWHP2yFS
+2hyDNC8Oy5qlrL5GjbTbqpX65IkjYa8IfWbvn036DofU2vCYnQQZW25Egmrek+K1
+J3hR8sxHyOFsGBcV7UDgzuQD8UtDzfalQ0DvmqTUgNl00gT3B8tV/eHmL8XPlQID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBAHZ/un7lY0QzL7NSpF4xr5ThtNQyxkGDb63B
+etmaRrmTyH1dhZ7haXrYC12biE9r5H4mMo01SdOIkwpbN3zabGxKJM5bB627hXW3
++DAsS/a3riKU5lQHByADwXhblbFFOMEUiDqb80ukhemRtE3fE+qV9jeLYhc/EO9c
+nmX6gNkZ
 -----END CERTIFICATE-----
@@ -1,15 +1,15 @@
 -----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQCwIlSufhfNTPhuyO+ZMM6aR090jl9pvAhZen1RL+2heDQgXXu1
-MXVMmMNKGkjeVnJdOPgM/TyC11FBUmc7LWApdGduHz18v5b1jM2HxIgzYWnJWEDU
-EwVbgTHQgyikcJ1hJNSs1oXSqJpamrWKZaBMApyzbfNIgvOBJyhvqTMt5QIDAQAB
-AoGAcCQLgmVpxh6hdfedBQ3cHMIWRoF3IhzDvCSOsSgwxcOAfLLIVp68rh39CKsf
-hd/IRK5G0A5TfoKUi2BptXW4aN3Y4KduO9+WJJMzx7HDjDfzKl3i+ozwceEo1vPJ
-91dlEMqerBra3z0+m/SRPGW67+6Z+cRe2rZ58zFu8ymH9akCQQDbUIvD3eQBmrEL
-reA6jufnWXHjziW+6VlDdRYZ5H3lpCKHEXpoyDc4Pxd9S/8zdIULVnxrYH8c5K43
-2PBLY6NzAkEAzZi3L/RxQbWc58CNZ9E7Tq3U9rD87x3q/a4/jgkMe/O1niVrCReA
-H710sWAQoczRUzfIUMUHIsMkMast4x+DRwJBAJQgSsX30E/lEz84KQJkEu2G1A1w
-5r2TDd8p7ct/MCkyOr/PZkXvNJygwFS4R52RTWri9xXvDuuM6L5/n4zLt1ECQQDB
-4SPO2yr6ljNS8OfoYQ0WkyLjR+Ad9g6mD23rmWjmctpwOkUHm9u9EsI6iqbkjetQ
-GKiUUlj6VR+Knyx0pED9AkEAl9y0Baa/z5MKeQiZ3lytT5MOBV2+4drXEi9I7Iw7
-lbc6+AgFR4URp2RmDCOG4e6ghSLd2wL/1vCcyiw9Q+10AQ==
+MIICXAIBAAKBgQC74ZFnrU3GHG/XdPih2bdMLXKlKL86dTsdt5y4dWHP2yFS2hyD
+NC8Oy5qlrL5GjbTbqpX65IkjYa8IfWbvn036DofU2vCYnQQZW25Egmrek+K1J3hR
+8sxHyOFsGBcV7UDgzuQD8UtDzfalQ0DvmqTUgNl00gT3B8tV/eHmL8XPlQIDAQAB
+AoGAaeEbtWa4Dq5V5QD5YRS1tItkMWk+Iy6PqxafjmAwSBgAtpVOPi55WogpVbZ8
+4HvZcXW34dVK55KUNl+dU+rd4NaDpka8RBjY99c1sJz7CmtPh9G3HHvcq0LIHZ+o
+zDDLOwY5501czxJ+H6nwf3tmiZlGVqd7D4UQsG3ZWSBdVH0CQQDv2zYFX3H2sMZV
+ntLvL00XizzNinQTK5Znnjpb4ZpAQFJcGTZa0jahc/8cTQRqcGfJATg4lLEcs+Jy
+C1Kroe+zAkEAyIbPi8ONBRJA+pWoPvSRBNRhmboWVJ9424t8mLzXhwyo+NOYb3f8
+6Rm2YNfkUB/DEJTwhxmRPeL2fCPT4HpflwJAc805KqTnJ7w3NXVFz7bdmIVf6Lh0
+zCCaMBLDiELGr0ieutptVLHzMEYJoAdLH3x/jxsooqCfVgU/SeJPmo/HYwJARaPL
+4FYBW4mDV4Mx8usskejQHHsr1ier6VL/6mtqzlPvOrsBbXTWOH3dgLR5bfoZ3GBd
+SA3xCvUPdP1cT9Ev8QJBANIsQHxb4VsyRvz5La0oNwrjzp9BrV8t7xf4Sz46T/6m
+uVl7tyb/M2x6qpYmz1u9D3hUDSysssd0eJbtCbfuU/0=
 -----END RSA PRIVATE KEY-----
@@ -1,12 +1,12 @@
 -----BEGIN CERTIFICATE-----
-MIIBsjCCARsCCQCiVUE8+nXmejANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV
-UzELMAkGA1UEAxMCY2EwHhcNMTIwNDE4MTczNzI1WhcNMzIwNDEzMTczNzI1WjAh
-MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQDGYovJ6A5gUVPAWv3RvJ05tk2CLYz0y8bN07ycJZqfYZaa
-dAUfAsr2wjxT3vWNJNglfk9MlEIODFiUtZXwRcayrdE4gDUFxF2g8THy79EFktZd
-Ct2L0NxDiavVoPrvZlgSY49JgLux5FhMD0zyouCU+npmJVvtoemZSzG4LpwWzQID
-AQABMA0GCSqGSIb3DQEBBQUAA4GBAEwCcjQDSpEgWCltyToax1fVB0n+5idAQTS7
-giIkcsJeN0ixut0dqQf8cddzxXLvn5R8rwsmiPv3LsT3djXimrMPaRJI7uJEIp5X
-kJfab0TT+k2fRCUdr99YUN4PTDbEQ+V5qszibNN4HtN4e1FRcteHuBpZHj5k4KF9
-qkrFmHc/
+MIIBsjCCARsCCQCptEBZlSnk3jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV
+UzELMAkGA1UEAxMCY2EwHhcNMTQxMjEyMDUwMzI1WhcNMzQxMjA3MDUwMzI1WjAh
+MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJMTI3LjAuMC4xMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDDhbj7nsfzahPilwn6pGdo6nKYCR21WZ73CuwPN86DmsZi
+5LIRYRfKA0unape2BQBnMnSmInaXvHHBdVsTyt3XSFZj5+iCF9RcorXAqcDygScj
+8MTWYAZxCu3lGAjtw0bGGYutlLg5jtEXvZwfe61XfJj9xDUPNQrP7mf/HTBmgQID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBACRIx9fB4x8UO44C9TGj3bKb1NX3bkuHMz0m
+WdhCkzUUiANtRMxp2oLA3KHY4yOusZLZIUNyP10Ri5q/U1mR0poYCMm7AYee2OV7
+NdQIyppeDLoWQ9uPISPjp1d+zjpGOrLrSkpD1rYLVw4R56A9ZQks/LNs6TSceZjZ
+c5QST/9i
 -----END CERTIFICATE-----
@@ -1,15 +1,15 @@
 -----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDGYovJ6A5gUVPAWv3RvJ05tk2CLYz0y8bN07ycJZqfYZaadAUf
-Asr2wjxT3vWNJNglfk9MlEIODFiUtZXwRcayrdE4gDUFxF2g8THy79EFktZdCt2L
-0NxDiavVoPrvZlgSY49JgLux5FhMD0zyouCU+npmJVvtoemZSzG4LpwWzQIDAQAB
-AoGBALCvE9C+3A7sX6rxXx1s3pqHwoAuEVv0IRsveMuYsh1AzODCc80qr5kVZKVN
-7WPHbLn3xKhvYmUgmcPX9bIIcu0eQAkAvHF/k99FHsokN768Dy5f35NckAE0pgg7
-53rb2N6mswz6jCprd+pEogwKgSFtjTAJe0TMHuvDpVFlTwABAkEA6MHCKsGMADXQ
-4LnGI3arn/UvUaVmg3d0TtbMkXAklatl8hcU6lTNK9+PQQaehrUerG05G4fqkf2f
-8Qc48+fTzQJBANoyGNtSacifiX+ixlQ0Taa/lsfBLyTRT313k0w+wzi6c75N352X
-AxK0/jly4xKRceO942UlojALupqnCUm4TwECQQCIpwFlct7MXMw2tDtFJO4caqoO
-aGpocyChDPFrzghcqxWebbjE32baBFYp2kYGVp4co7+sjz7D2cEmlJP2dzxdAkBG
-cvZjmfBdvmT+uOegOGL5+9ILHOzDznED6Z+9dmZB4rGMOEiC8Uckuxt6IQX/8MX8
-6kqJVeVP7fmKBXSSQNgBAkEAiihs3rRJr6AyXJwkrJv/ouwaTDpcDUBL/8TGzibs
-TNZHAVwOKvjA4y5vw3bBlltuiXDey5C8fo/4Gre/rcB/tg==
+MIICXAIBAAKBgQDDhbj7nsfzahPilwn6pGdo6nKYCR21WZ73CuwPN86DmsZi5LIR
+YRfKA0unape2BQBnMnSmInaXvHHBdVsTyt3XSFZj5+iCF9RcorXAqcDygScj8MTW
+YAZxCu3lGAjtw0bGGYutlLg5jtEXvZwfe61XfJj9xDUPNQrP7mf/HTBmgQIDAQAB
+AoGBAKqQ+XLobV435+kAAkCZM20fOaDQMw6FhFvXN19/O7UrTG3xPDZVET+16EXA
+g2DStCtSpNhZsENmrYYrLqzxiNxDHagmEizmicdjoU2sXCHUbLtSojwedGTixBNf
+ofuDPi/j7T24CQFK34ROwA4diMVsD8YBLOv+jXZMXaq1ROwBAkEA6RfoF2P9r7MW
+Q5Yq54eZdU6viQmHXV8ePe43NmcEXGUMKb049RHekCWggvAgcTz3bzLIP4v6SQxz
+J17wezU7sQJBANa8nYGOX29/TF2LS4vpAf85jPa+MqeFVOHXunlZbibXwPvx6WWS
+EwwimDX6Q0q+dA4XqSDrb622BED5A18fG9ECQHOT1xFCE1g7hJ6ep0eGhSEVkxRw
+FR7HO0eaBuaCzjbCI/XOTZ+27JStE5nZVzzO7iHFHGXmEoSGF9M90CBHlQECQH/0
+Ffpb5pweg1d+J/7vUcRN+6QPQ0m01hZYoIGse0lj6Fd4F3Xxa64gcwg+3pgMEkVP
+bfTytTISMkiw2IR2mfECQE343mUbDSOGGoDAN5ByWydkwfqaJvNBCHaBAkJ6lthU
+4yVXQabDGkZMh1jdxi8hK/TRLiNgGks8tgFp6Zqc0SU=
 -----END RSA PRIVATE KEY-----
@@ -12,19 +12,19 @@ push @$collection, 3, 4, 5;
 is_deeply [@$collection], [1, 2, 3, 4, 5], 'right result';
 
 # Tap into method chain
-is_deeply [c(1, 2, 3)->tap(sub { $_->[1] += 2 })->each], [1, 4, 3],
+is_deeply c(1, 2, 3)->tap(sub { $_->[1] += 2 })->to_array, [1, 4, 3],
   'right result';
 
 # compact
-is_deeply [c(undef, 0, 1, '', 2, 3)->compact->each], [0, 1, 2, 3],
+is_deeply c(undef, 0, 1, '', 2, 3)->compact->to_array, [0, 1, 2, 3],
   'right result';
-is_deeply [c(3, 2, 1)->compact->each], [3, 2, 1], 'right result';
-is_deeply [c()->compact->each], [], 'right result';
+is_deeply c(3, 2, 1)->compact->to_array, [3, 2, 1], 'right result';
+is_deeply c()->compact->to_array, [], 'right result';
 
 # flatten
-is_deeply [c(1, 2, [3, 4], 5, c(6, 7))->flatten->each], [1, 2, 3, 4, 5, 6, 7],
-  'right result';
-is_deeply [c(undef, 1, [2, {}, [3, c(4, 5)]], undef, 6)->flatten->each],
+is_deeply c(1, 2, [3, 4], 5, c(6, 7))->flatten->to_array,
+  [1, 2, 3, 4, 5, 6, 7], 'right result';
+is_deeply c(undef, 1, [2, {}, [3, c(4, 5)]], undef, 6)->flatten->to_array,
   [undef, 1, 2, {}, 3, 4, 5, undef, 6], 'right result';
 
 # each
@@ -48,7 +48,7 @@ is $collection->first(qr/[1-4]/), 4, 'right result';
 is $collection->first(sub { ref $_ eq 'CODE' }), undef, 'no result';
 $collection = c();
 is $collection->first, undef, 'no result';
-is $collection->first(sub { defined $_ }), undef, 'no result';
+is $collection->first(sub {defined}), undef, 'no result';
 
 # last
 is c(5, 4, 3)->last, 3, 'right result';
@@ -57,16 +57,18 @@ is c()->last, undef, 'no result';
 
 # grep
 $collection = c(1, 2, 3, 4, 5, 6, 7, 8, 9);
-is_deeply [$collection->grep(qr/[6-9]/)->each], [6, 7, 8, 9], 'right elements';
-is_deeply [$collection->grep(sub {/[6-9]/})->each], [6, 7, 8, 9],
+is_deeply $collection->grep(qr/[6-9]/)->to_array, [6, 7, 8, 9],
+  'right elements';
+is_deeply $collection->grep(sub {/[6-9]/})->to_array, [6, 7, 8, 9],
+  'right elements';
+is_deeply $collection->grep(sub { $_ > 5 })->to_array, [6, 7, 8, 9],
   'right elements';
-is_deeply [$collection->grep(sub { $_ > 5 })->each], [6, 7, 8, 9],
+is_deeply $collection->grep(sub { $_ < 5 })->to_array, [1, 2, 3, 4],
   'right elements';
-is_deeply [$collection->grep(sub { $_ < 5 })->each], [1, 2, 3, 4],
+is_deeply $collection->grep(sub { shift == 5 })->to_array, [5],
   'right elements';
-is_deeply [$collection->grep(sub { shift == 5 })->each], [5], 'right elements';
-is_deeply [$collection->grep(sub { $_ < 1 })->each], [], 'no elements';
-is_deeply [$collection->grep(sub { $_ > 9 })->each], [], 'no elements';
+is_deeply $collection->grep(sub { $_ < 1 })->to_array, [], 'no elements';
+is_deeply $collection->grep(sub { $_ > 9 })->to_array, [], 'no elements';
 
 # join
 $collection = c(1, 2, 3);
@@ -75,8 +77,6 @@ is $collection->join(''),    '123',       'right result';
 is $collection->join('---'), '1---2---3', 'right result';
 is $collection->join("\n"),  "1\n2\n3",   'right result';
 is $collection->join('/')->url_escape, '1%2F2%2F3', 'right result';
-$collection = c(c(1, 2, 3), c(3, 2, 1));
-is $collection->join(''), "1\n2\n33\n2\n1", 'right result';
 
 # map
 $collection = c(1, 2, 3);
@@ -84,21 +84,26 @@ is $collection->map(sub { $_ + 1 })->join(''), '234', 'right result';
 is_deeply [@$collection], [1, 2, 3], 'right elements';
 is $collection->map(sub { shift() + 2 })->join(''), '345', 'right result';
 is_deeply [@$collection], [1, 2, 3], 'right elements';
+$collection = c(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9));
+is $collection->map('reverse')->map(join => "\n")->join("\n"),
+  "3\n2\n1\n6\n5\n4\n9\n8\n7", 'right result';
+is $collection->map(join => '-')->join("\n"), "1-2-3\n4-5-6\n7-8-9",
+  'right result';
 
 # reverse
 $collection = c(3, 2, 1);
-is_deeply [$collection->reverse->each], [1, 2, 3], 'right order';
+is_deeply $collection->reverse->to_array, [1, 2, 3], 'right order';
 $collection = c(3);
-is_deeply [$collection->reverse->each], [3], 'right order';
+is_deeply $collection->reverse->to_array, [3], 'right order';
 $collection = c();
-is_deeply [$collection->reverse->each], [], 'no elements';
+is_deeply $collection->reverse->to_array, [], 'no elements';
 
 # shuffle
 $collection = c(0 .. 10000);
 my $random = $collection->shuffle;
 is $collection->size, $random->size, 'same number of elements';
 isnt "@$collection", "@$random", 'different order';
-is_deeply [c()->shuffle->each], [], 'no elements';
+is_deeply c()->shuffle->to_array, [], 'no elements';
 
 # size
 $collection = c();
@@ -112,60 +117,41 @@ 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 { $_[1] cmp $_[0] })->each], [5, 4, 2, 1],
+is_deeply $collection->sort->to_array, [1, 2, 4, 5], 'right order';
+is_deeply $collection->sort(sub { $b cmp $a })->to_array, [5, 4, 2, 1],
+  'right order';
+is_deeply $collection->sort(sub { $_[1] cmp $_[0] })->to_array, [5, 4, 2, 1],
   'right order';
 $collection = c(qw(Test perl Mojo));
-is_deeply [$collection->sort(sub { uc(shift) cmp uc(shift) })->each],
+is_deeply $collection->sort(sub { uc(shift) cmp uc(shift) })->to_array,
   [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->to_array, [], 'no elements';
+is_deeply $collection->sort(sub { $a cmp $b })->to_array, [], 'no elements';
 
 # slice
 $collection = c(1, 2, 3, 4, 5, 6, 7, 10, 9, 8);
-is_deeply [$collection->slice(0)->each],  [1], 'right result';
-is_deeply [$collection->slice(1)->each],  [2], 'right result';
-is_deeply [$collection->slice(2)->each],  [3], 'right result';
-is_deeply [$collection->slice(-1)->each], [8], 'right result';
-is_deeply [$collection->slice(-3, -5)->each], [10, 6], 'right result';
-is_deeply [$collection->slice(1, 2, 3)->each], [2, 3, 4], 'right result';
-is_deeply [$collection->slice(6, 1, 4)->each], [7, 2, 5], 'right result';
-is_deeply [$collection->slice(6 .. 9)->each], [7, 10, 9, 8], 'right result';
-
-# pluck
-$collection = c(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9));
-is $collection->pluck('reverse'), "3\n2\n1\n6\n5\n4\n9\n8\n7", 'right result';
-is $collection->pluck(join => '-'), "1-2-3\n4-5-6\n7-8-9", 'right result';
-$collection = c(b('one'), b('two'), b('three'));
-is $collection->camelize, "One\nTwo\nThree", 'right result';
-is $collection->url_escape('^netwhr')->reverse, "%54hree\n%54w%6F\n%4Fne",
-  'right result';
+is_deeply $collection->slice(0)->to_array,  [1], 'right result';
+is_deeply $collection->slice(1)->to_array,  [2], 'right result';
+is_deeply $collection->slice(2)->to_array,  [3], 'right result';
+is_deeply $collection->slice(-1)->to_array, [8], 'right result';
+is_deeply $collection->slice(-3, -5)->to_array, [10, 6], 'right result';
+is_deeply $collection->slice(1, 2, 3)->to_array, [2, 3, 4], 'right result';
+is_deeply $collection->slice(6, 1, 4)->to_array, [7, 2, 5], 'right result';
+is_deeply $collection->slice(6 .. 9)->to_array, [7, 10, 9, 8], 'right result';
 
 # uniq
 $collection = c(1, 2, 3, 2, 3, 4, 5, 4);
-is_deeply [$collection->uniq->each], [1, 2, 3, 4, 5], 'right result';
-is_deeply [$collection->uniq->reverse->uniq->each], [5, 4, 3, 2, 1],
+is_deeply $collection->uniq->to_array, [1, 2, 3, 4, 5], 'right result';
+is_deeply $collection->uniq->reverse->uniq->to_array, [5, 4, 3, 2, 1],
   'right result';
 
-# Missing method and function (AUTOLOAD)
-eval { Mojo::Collection->new(b('whatever'))->missing };
-like $@,
-  qr/^Can't locate object method "missing" via package "Mojo::ByteStream"/,
-  'right error';
-eval { Mojo::Collection->new(undef)->missing };
-like $@, qr/^Can't call method "missing" on an undefined value/, 'right error';
-eval { Mojo::Collection::missing() };
-like $@, qr/^Undefined subroutine &Mojo::Collection::missing called/,
-  'right error';
-
-# Boolean context
-$collection = Mojo::Collection->new;
-ok !$collection, 'empty collection is falsy';
-$collection = Mojo::Collection->new('wat');
-ok $collection, ' non-empty collection is truthy ';
-
 done_testing();
@@ -82,7 +82,7 @@ is $content->charset, 'UTF-8', 'right charset';
 $content->headers->content_type('text/plain; charset  =  "UTF-8"');
 is $content->charset, 'UTF-8', 'right charset';
 
-# Partial content with 128bit content length
+# Partial content with 128-bit content length
 $content = Mojo::Content::Single->new;
 $content->parse(
   "Content-Length: 18446744073709551616\x0d\x0a\x0d\x0aHello World!");
@@ -344,7 +344,7 @@ $jar->extract($tx);
 $tx = Mojo::Transaction::HTTP->new;
 $tx->req->url->parse('http://example.com/test');
 $jar->inject($tx);
-@cookies = $tx->req->cookie('foo');
+@cookies = @{$tx->req->every_cookie('foo')};
 is $cookies[0]->name,  'foo',     'right name';
 is $cookies[0]->value, 'without', 'right value';
 is $cookies[1]->name,  'foo',     'right name';
@@ -353,7 +353,7 @@ is $cookies[2], undef, 'no third cookie';
 $tx = Mojo::Transaction::HTTP->new;
 $tx->req->url->parse('http://www.example.com/test');
 $jar->inject($tx);
-@cookies = $tx->req->cookie('foo');
+@cookies = @{$tx->req->every_cookie('foo')};
 is $cookies[0]->name,  'foo',  'right name';
 is $cookies[0]->value, 'with', 'right value';
 is $cookies[1], undef, 'no second cookie';
@@ -1,12 +1,11 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
+use Cwd 'abs_path';
 use File::Spec::Functions 'catdir';
+use FindBin;
 use Mojo;
 use Mojo::IOLoop;
 use Mojo::Log;
@@ -14,6 +13,25 @@ use Mojo::Server::Daemon;
 use Mojo::UserAgent;
 use Mojolicious;
 
+package TestApp;
+use Mojo::Base 'Mojo';
+
+sub handler {
+  my ($self, $tx) = @_;
+  $tx->res->code(200);
+  $tx->res->body('Hello TestApp!');
+  $tx->resume;
+}
+
+package main;
+
+# Minimal application
+my $ua = Mojo::UserAgent->new;
+$ua->server->app(TestApp->new);
+my $tx = $ua->get('/');
+is $tx->res->code, 200, 'right status';
+is $tx->res->body, 'Hello TestApp!', 'right content';
+
 # Timeout
 {
   is(Mojo::Server::Daemon->new->inactivity_timeout, 15, 'right value');
@@ -27,9 +45,9 @@ use Mojolicious;
 {
   is_deeply(Mojo::Server::Daemon->new->listen,
     ['http://*:3000'], 'right value');
-  local $ENV{MOJO_LISTEN} = 'http://localhost:8080';
+  local $ENV{MOJO_LISTEN} = 'http://127.0.0.1:8080';
   is_deeply(Mojo::Server::Daemon->new->listen,
-    ['http://localhost:8080'], 'right value');
+    ['http://127.0.0.1:8080'], 'right value');
   $ENV{MOJO_LISTEN} = 'http://*:80,https://*:443';
   is_deeply(
     Mojo::Server::Daemon->new->listen,
@@ -61,12 +79,29 @@ is $app->config({test => 23})->config->{test}, 23, 'right value';
 is_deeply $app->config, {foo => 'bar', baz => 'yada', test => 23},
   'right value';
 
+# Script name
+my $path = "$FindBin::Bin/lib/../lib/myapp.pl";
+is(Mojo::Server::Daemon->new->load_app($path)->config('script'),
+  abs_path($path), 'right script name');
+
+# Load broken app
+eval {
+  Mojo::Server::Daemon->new->load_app(
+    "$FindBin::Bin/lib/Mojo/LoaderException.pm");
+};
+like $@, qr/^Can't load application/, 'right error';
+
+# Load missing application class
+eval { Mojo::Server::Daemon->new->build_app('Mojo::DoesNotExist') };
+like $@, qr/^Can't find application class "Mojo::DoesNotExist" in \@INC/,
+  'right error';
+
 # Transaction
 isa_ok $app->build_tx, 'Mojo::Transaction::HTTP', 'right class';
 
 # Fresh application
 $app = Mojolicious->new;
-my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+$ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
 is $ua->server->app($app)->app->moniker, 'mojolicious', 'right moniker';
 
 # Silence
@@ -105,7 +140,7 @@ $app->routes->post(
 $app->routes->any('/*whatever' => {text => 'Whatever!'});
 
 # Normal request
-my $tx = $ua->get('/normal/');
+$tx = $ua->get('/normal/');
 ok $tx->keep_alive, 'will be kept alive';
 is $tx->res->code, 200,         'right status';
 is $tx->res->body, 'Whatever!', 'right content';
@@ -172,32 +207,29 @@ my %params;
 for my $i (1 .. 10) { $params{"test$i"} = $i }
 my $result = '';
 for my $key (sort keys %params) { $result .= $params{$key} }
-my ($code, $body);
-my $port = $ua->server->url->port;
-$tx = $ua->post("http://127.0.0.1:$port/chunked" => form => \%params);
+$tx = $ua->post('/chunked' => form => \%params);
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, $result, 'right content';
 
 # Upload
-($code, $body) = ();
-$tx = $ua->post(
-  "http://127.0.0.1:$port/upload" => form => {file => {content => $result}});
+$tx = $ua->post('/upload' => form => {file => {content => $result}});
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, $result, 'right content';
 ok $tx->local_address, 'has local address';
 ok $tx->local_port > 0, 'has local port';
-ok $tx->remote_address, 'has local address';
-ok $tx->remote_port > 0, 'has local port';
+ok $tx->original_remote_address, 'has original remote address';
+ok $tx->remote_address,          'has remote address';
+ok $tx->remote_port > 0, 'has remote port';
 ok $local_address, 'has local address';
 ok $local_port > 0, 'has local port';
-ok $remote_address, 'has local address';
-ok $remote_port > 0, 'has local port';
+ok $remote_address, 'has remote address';
+ok $remote_port > 0, 'has remote port';
 
 # Pipelined
 my $daemon
   = Mojo::Server::Daemon->new(listen => ['http://127.0.0.1'], silent => 1);
 $daemon->start;
-$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->port;
 is $daemon->app->moniker, 'HelloWorld', 'right moniker';
 my $buffer = '';
 my $id;
@@ -228,28 +260,15 @@ $daemon = Mojo::Server::Daemon->new(
   silent => 1
 );
 is scalar @{$daemon->acceptors}, 0, 'no active acceptors';
-$daemon->ioloop->max_connections(500);
-$daemon->start;
-is $daemon->ioloop->max_connections, 500, 'right number';
-is scalar @{$daemon->acceptors}, 1, 'one active acceptor';
-is $daemon->app->moniker, 'mojolicious', 'right moniker';
-$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
-$tx = $ua->get("http://127.0.0.1:$port/throttle1" => {Connection => 'close'});
-ok $tx->success, 'successful';
-is $tx->res->code, 200,         'right status';
-is $tx->res->body, 'Whatever!', 'right content';
-$daemon->stop;
-is scalar @{$daemon->acceptors}, 0, 'no active acceptors';
-$tx = $ua->inactivity_timeout(0.5)
-  ->get("http://127.0.0.1:$port/throttle2" => {Connection => 'close'});
-ok !$tx->success, 'not successful';
-is $tx->error->{message}, 'Inactivity timeout', 'right error';
-$daemon->max_clients(600)->start;
-is $daemon->ioloop->max_connections, 600, 'right number';
-$tx = $ua->inactivity_timeout(10)
-  ->get("http://127.0.0.1:$port/throttle3" => {Connection => 'close'});
-ok $tx->success, 'successful';
-is $tx->res->code, 200,         'right status';
-is $tx->res->body, 'Whatever!', 'right content';
+is scalar @{$daemon->start->acceptors}, 1, 'one active acceptor';
+$id = $daemon->acceptors->[0];
+ok !!Mojo::IOLoop->acceptor($id), 'acceptor has been added';
+is scalar @{$daemon->stop->acceptors}, 0, 'no active acceptors';
+ok !Mojo::IOLoop->acceptor($id), 'acceptor has been removed';
+is scalar @{$daemon->start->acceptors}, 1, 'one active acceptor';
+$id = $daemon->acceptors->[0];
+ok !!Mojo::IOLoop->acceptor($id), 'acceptor has been added';
+undef $daemon;
+ok !Mojo::IOLoop->acceptor($id), 'acceptor has been removed';
 
 done_testing();
@@ -9,6 +9,32 @@ 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('2014-08-20  20:45:00')->epoch,
+  1408567500, '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 +61,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 +70,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';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -4,11 +4,11 @@ use Test::More;
 use Mojo::DOM;
 
 # Simple (basics)
-my $dom = Mojo::DOM->new->parse(
+my $dom = Mojo::DOM->new(
   '<div><div FOO="0" id="a">A</div><div id="b">B</div></div>');
 is $dom->at('#b')->text, 'B', 'right text';
 my @div;
-push @div, $dom->find('div[id]')->text->each;
+push @div, $dom->find('div[id]')->map('text')->each;
 is_deeply \@div, [qw(A B)], 'found all div elements with id';
 @div = ();
 $dom->find('div[id]')->each(sub { push @div, $_->text });
@@ -20,12 +20,13 @@ is "$dom", '<div><div foo="0" id="a">A</div><div id="b">B</div></div>',
 
 # Tap into method chain
 $dom = Mojo::DOM->new->parse('<div id="a">A</div><div id="b">B</div>');
-is_deeply [$dom->find('[id]')->attr('id')->each], [qw(a b)], 'right result';
+is_deeply [$dom->find('[id]')->map(attr => 'id')->each], [qw(a b)],
+  'right result';
 is $dom->tap(sub { $_->at('#b')->remove }), '<div id="a">A</div>',
   'right result';
 
 # Simple nesting with healing (tree structure)
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <foo><bar a="b&lt;c">ju<baz a23>s<bazz />t</bar>works</foo>
 EOF
 is $dom->tree->[0], 'root', 'right element';
@@ -62,7 +63,7 @@ is "$dom", <<EOF, 'right result';
 EOF
 
 # Select based on parent
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <body>
   <div>test1</div>
   <div><div>test2</div></div>
@@ -142,13 +143,30 @@ is $dom->next,     undef, 'no siblings';
 is $dom->previous, undef, 'no siblings';
 is $dom->at('foo > a')->next,          undef, 'no next sibling';
 is $dom->at('foo > simple')->previous, undef, 'no previous sibling';
-is_deeply [$dom->at('simple')->ancestors->type->each], ['foo'],
+is_deeply [$dom->at('simple')->ancestors->map('type')->each], ['foo'],
   'right results';
 ok !$dom->at('simple')->ancestors->first->xml, 'XML mode not active';
 
 # Nodes
 $dom = Mojo::DOM->new(
   '<!DOCTYPE before><p>test<![CDATA[123]]><!-- 456 --></p><?after?>');
+is $dom->at('p')->preceding_siblings->first->content, ' before',
+  'right content';
+is $dom->at('p')->preceding_siblings->size, 1, 'right number of nodes';
+is $dom->at('p')->contents->last->preceding_siblings->first->content, 'test',
+  'right content';
+is $dom->at('p')->contents->last->preceding_siblings->last->content, '123',
+  'right content';
+is $dom->at('p')->contents->last->preceding_siblings->size, 2,
+  'right number of nodes';
+is $dom->preceding_siblings->size, 0, 'no preceding nodes';
+is $dom->at('p')->following_siblings->first->content, 'after', 'right content';
+is $dom->at('p')->following_siblings->size, 1, 'right number of nodes';
+is $dom->contents->first->following_siblings->first->type, 'p', 'right type';
+is $dom->contents->first->following_siblings->last->content, 'after',
+  'right content';
+is $dom->contents->first->following_siblings->size, 2, 'right number of nodes';
+is $dom->following_siblings->size, 0, 'no following nodes';
 is $dom->at('p')->previous_sibling->content, ' before', 'right content';
 is $dom->at('p')->previous_sibling->previous_sibling, undef,
   'no more siblings';
@@ -185,7 +203,7 @@ is $dom->contents->[2]->node,    'pi',    'right node';
 is $dom->contents->[2]->content, 'after', 'right content';
 is $dom->contents->first->content(' again')->content, ' again',
   'right content';
-is $dom->contents->grep(sub { $_->node eq 'pi' })->remove->first->node,
+is $dom->contents->grep(sub { $_->node eq 'pi' })->map('remove')->first->node,
   'root', 'right node';
 is "$dom", '<!DOCTYPE again><p><![CDATA[123]]><!-- 456 --></p>',
   'right result';
@@ -202,7 +220,7 @@ is "$dom", '<script>a<b>c</b>1<b>d</b></script>', 'right result';
 is $dom->at('b')->contents->first->append('e')->content, 'c', 'right content';
 is $dom->at('b')->contents->first->prepend('f')->node, 'text', 'right node';
 is "$dom", '<script>a<b>fce</b>1<b>d</b></script>', 'right result';
-is $dom->at('script')->contents->first->siblings->first->type, 'b',
+is $dom->at('script')->contents->first->following->first->type, 'b',
   'right type';
 is $dom->at('script')->contents->first->next->content, 'fce', 'right content';
 is $dom->at('script')->contents->first->previous, undef, 'no siblings';
@@ -213,14 +231,35 @@ is $dom->at('script')->contents->first->wrap('<i>:)</i>')->root,
   '<script><i>:)a</i><b>fce</b>1<b>d</b></script>', 'right result';
 is $dom->at('i')->contents->first->wrap_content('<b></b>')->root,
   '<script><i><b>:)</b>a</i><b>fce</b>1<b>d</b></script>', 'right result';
-is $dom->at('b')->contents->first->ancestors->type->join(','), 'b,i,script',
-  'right result';
+is $dom->at('b')->contents->first->ancestors->map('type')->join(','),
+  'b,i,script', 'right result';
 is $dom->at('b')->contents->first->append_content('g')->content, ':)g',
   'right content';
 is $dom->at('b')->contents->first->prepend_content('h')->content, 'h:)g',
   'right content';
 is "$dom", '<script><i><b>h:)g</b>a</i><b>fce</b>1<b>d</b></script>',
   'right result';
+is $dom->at('script > b:last-of-type')->append('<!--y-->')
+  ->following_siblings->first->content, 'y', 'right content';
+is $dom->at('i')->prepend('z')->preceding_siblings->first->content, 'z',
+  'right content';
+is $dom->at('i')->following->last->text, 'd', 'right text';
+is $dom->at('i')->following->size, 2, 'right number of following elements';
+is $dom->at('i')->following('b:last-of-type')->first->text, 'd', 'right text';
+is $dom->at('i')->following('b:last-of-type')->size, 1,
+  'right number of following elements';
+is $dom->following->size, 0, 'no following elements';
+is $dom->at('script > b:last-of-type')->preceding->first->type, 'i',
+  'right type';
+is $dom->at('script > b:last-of-type')->preceding->size, 2,
+  'right number of preceding elements';
+is $dom->at('script > b:last-of-type')->preceding('b')->first->type, 'b',
+  'right type';
+is $dom->at('script > b:last-of-type')->preceding('b')->size, 1,
+  'right number of preceding elements';
+is $dom->preceding->size, 0, 'no preceding elements';
+is "$dom", '<script>z<i><b>h:)g</b>a</i><b>fce</b>1<b>d</b><!--y--></script>',
+  'right result';
 
 # XML nodes
 $dom = Mojo::DOM->new->xml(1)->parse('<b>test<image /></b>');
@@ -245,11 +284,11 @@ is $dom->contents->first->text,      '', 'no text';
 is $dom->contents->first->all_text,  '', 'no text';
 
 # Class and ID
-$dom = Mojo::DOM->new->parse('<div id="id" class="class">a</div>');
+$dom = Mojo::DOM->new('<div id="id" class="class">a</div>');
 is $dom->at('div#id.class')->text, 'a', 'right text';
 
 # Deep nesting (parent combinator)
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <title>Foo</title>
@@ -283,27 +322,19 @@ $dom->find('p')->each(sub { push @p, $_->attr('id') });
 is_deeply \@p, [qw(foo bar)], 'found all p elements';
 my $ids = [qw(container header logo buttons buttons content)];
 is_deeply \@div, $ids, 'found all div elements';
-is_deeply [$dom->at('p')->ancestors->type->each], [qw(div div div body html)],
-  'right results';
+is_deeply [$dom->at('p')->ancestors->map('type')->each],
+  [qw(div div div body html)], 'right results';
 is_deeply [$dom->at('html')->ancestors->each], [], 'no results';
 is_deeply [$dom->ancestors->each],             [], 'no results';
-is_deeply [$dom->siblings->each],              [], 'no results';
-ok $dom->at('form')->siblings->[0]->match('#header'),  'right sibling';
-ok $dom->at('form')->siblings->[1]->match('#content'), 'right sibling';
-is $dom->at('form')->siblings('#content')->first->text, 'More stuff',
-  'right text';
-is_deeply [$dom->at('form')->siblings('#nothing')->each], [], 'no results';
-is_deeply [$dom->at('#header')->siblings->type->each], [qw(form div)],
-  'right results';
 
 # Script tag
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <script charset="utf-8">alert('lalala');</script>
 EOF
 is $dom->at('script')->text, "alert('lalala');", 'right script content';
 
 # HTML5 (unquoted values)
-$dom = Mojo::DOM->new->parse(
+$dom = Mojo::DOM->new(
   '<div id = test foo ="bar" class=tset bar=/baz/ baz=//>works</div>');
 is $dom->at('#test')->text,                'works', 'right text';
 is $dom->at('div')->text,                  'works', 'right text';
@@ -316,8 +347,8 @@ is $dom->at('[bar=/baz/]')->text, 'works', 'right text';
 is $dom->at('[baz=//]')->text,    'works', 'right text';
 
 # HTML1 (single quotes, uppercase tags and whitespace in attributes)
-$dom = Mojo::DOM->new->parse(
-  q{<DIV id = 'test' foo ='bar' class= "tset">works</DIV>});
+$dom
+  = Mojo::DOM->new(q{<DIV id = 'test' foo ='bar' class= "tset">works</DIV>});
 is $dom->at('#test')->text,       'works', 'right text';
 is $dom->at('div')->text,         'works', 'right text';
 is $dom->at('[foo="bar"]')->text, 'works', 'right text';
@@ -327,7 +358,7 @@ is $dom->at('[foo=ba]'), undef, 'no result';
 is $dom->at('.tset')->text, 'works', 'right text';
 
 # Already decoded Unicode snowman and quotes in selector
-$dom = Mojo::DOM->new->parse('<div id="snowm&quot;an">☃</div>');
+$dom = Mojo::DOM->new('<div id="snowm&quot;an">☃</div>');
 is $dom->at('[id="snowm\"an"]')->text,      '☃', 'right text';
 is $dom->at('[id="snowm\22 an"]')->text,    '☃', 'right text';
 is $dom->at('[id="snowm\000022an"]')->text, '☃', 'right text';
@@ -409,23 +440,23 @@ is $dom->to_string, $html, 'right result';
 is $dom->content,   $html, 'right result';
 
 # Looks remotely like HTML
-$dom = Mojo::DOM->new->parse(
+$dom = Mojo::DOM->new(
   '<!DOCTYPE H "-/W/D HT 4/E">☃<title class=test>♥</title>☃');
 is $dom->at('title')->text, '♥', 'right text';
 is $dom->at('*')->text,     '♥', 'right text';
 is $dom->at('.test')->text, '♥', 'right text';
 
 # Replace elements
-$dom = Mojo::DOM->new->parse('<div>foo<p>lalala</p>bar</div>');
+$dom = Mojo::DOM->new('<div>foo<p>lalala</p>bar</div>');
 is $dom->at('p')->replace('<foo>bar</foo>'),
   '<div>foo<foo>bar</foo>bar</div>', 'right result';
 is "$dom", '<div>foo<foo>bar</foo>bar</div>', 'right result';
-$dom->at('foo')->replace(Mojo::DOM->new->parse('text'));
+$dom->at('foo')->replace(Mojo::DOM->new('text'));
 is "$dom", '<div>footextbar</div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>foo</div><div>bar</div>');
+$dom = Mojo::DOM->new('<div>foo</div><div>bar</div>');
 $dom->find('div')->each(sub { shift->replace('<p>test</p>') });
 is "$dom", '<p>test</p><p>test</p>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>foo<p>lalala</p>bar</div>');
+$dom = Mojo::DOM->new('<div>foo<p>lalala</p>bar</div>');
 is $dom->replace('♥'), '♥', 'right result';
 is "$dom", '♥', 'right result';
 $dom->replace('<div>foo<p>lalala</p>bar</div>');
@@ -436,12 +467,12 @@ is $dom->replace(''), '', 'no result';
 is "$dom", '', 'no result';
 $dom->replace('<div>foo<p>lalala</p>bar</div>');
 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
-$dom->find('p')->replace('');
+$dom->find('p')->map(replace => '');
 is "$dom", '<div>foobar</div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>♥</div>');
+$dom = Mojo::DOM->new('<div>♥</div>');
 $dom->at('div')->content('☃');
 is "$dom", '<div>☃</div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>♥</div>');
+$dom = Mojo::DOM->new('<div>♥</div>');
 $dom->at('div')->content("\x{2603}");
 is $dom->to_string, '<div>☃</div>', 'right result';
 is $dom->at('div')->replace('<p>♥</p>')->root, '<p>♥</p>', 'right result';
@@ -450,32 +481,36 @@ is $dom->replace('<b>whatever</b>')->root, '<b>whatever</b>', 'right result';
 is $dom->to_string, '<b>whatever</b>', 'right result';
 $dom->at('b')->prepend('<p>foo</p>')->append('<p>bar</p>');
 is "$dom", '<p>foo</p><b>whatever</b><p>bar</p>', 'right result';
-is $dom->find('p')->remove->first->root->at('b')->text, 'whatever',
+is $dom->find('p')->map('remove')->first->root->at('b')->text, 'whatever',
   'right result';
 is "$dom", '<b>whatever</b>', 'right result';
 is $dom->at('b')->strip, 'whatever', 'right result';
 is $dom->strip,  'whatever', 'right result';
 is $dom->remove, '',         'right result';
 $dom->replace('A<div>B<p>C<b>D<i><u>E</u></i>F</b>G</p><div>H</div></div>I');
-is $dom->find(':not(div):not(i):not(u)')->strip->first->root,
+is $dom->find(':not(div):not(i):not(u)')->map('strip')->first->root,
   'A<div>BCD<i><u>E</u></i>FG<div>H</div></div>I', 'right result';
 is $dom->at('i')->to_string, '<i><u>E</u></i>', 'right result';
+$dom = Mojo::DOM->new('<div><div>A</div><div>B</div>C</div>');
+is $dom->at('div')->at('div')->text, 'A', 'right text';
+$dom->at('div')->find('div')->map('strip');
+is "$dom", '<div>ABC</div>', 'right result';
 
 # Replace element content
-$dom = Mojo::DOM->new->parse('<div>foo<p>lalala</p>bar</div>');
+$dom = Mojo::DOM->new('<div>foo<p>lalala</p>bar</div>');
 is $dom->at('p')->content('bar'), '<p>bar</p>', 'right result';
 is "$dom", '<div>foo<p>bar</p>bar</div>', 'right result';
-$dom->at('p')->content(Mojo::DOM->new->parse('text'));
+$dom->at('p')->content(Mojo::DOM->new('text'));
 is "$dom", '<div>foo<p>text</p>bar</div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>foo</div><div>bar</div>');
+$dom = Mojo::DOM->new('<div>foo</div><div>bar</div>');
 $dom->find('div')->each(sub { shift->content('<p>test</p>') });
 is "$dom", '<div><p>test</p></div><div><p>test</p></div>', 'right result';
 $dom->find('p')->each(sub { shift->content('') });
 is "$dom", '<div><p></p></div><div><p></p></div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div><p id="☃" /></div>');
+$dom = Mojo::DOM->new('<div><p id="☃" /></div>');
 $dom->at('#☃')->content('♥');
 is "$dom", '<div><p id="☃">♥</p></div>', 'right result';
-$dom = Mojo::DOM->new->parse('<div>foo<p>lalala</p>bar</div>');
+$dom = Mojo::DOM->new('<div>foo<p>lalala</p>bar</div>');
 $dom->content('♥');
 is "$dom", '♥', 'right result';
 is $dom->content('<div>foo<p>lalala</p>bar</div>'),
@@ -488,7 +523,7 @@ is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
 is $dom->at('p')->content(''), '<p></p>', 'right result';
 
 # Mixed search and tree walk
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
   <tr>
     <td>text1</td>
@@ -509,7 +544,7 @@ is $data[3], 'text2', 'right text';
 is $data[4], undef,   'no tag';
 
 # RSS
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <?xml version="1.0" encoding="UTF-8"?>
 <rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
   <channel>
@@ -536,7 +571,7 @@ $dom = Mojo::DOM->new->parse(<<EOF);
 EOF
 ok $dom->xml, 'XML mode detected';
 is $dom->find('rss')->[0]->attr('version'), '2.0', 'right version';
-is_deeply [$dom->at('title')->ancestors->pluck('type')->each],
+is_deeply [$dom->at('title')->ancestors->map('type')->each],
   [qw(channel rss)], 'right results';
 is $dom->at('extension')->attr('foo:id'), 'works', 'right id';
 like $dom->at('#works')->text,       qr/\[awesome\]\]/, 'right text';
@@ -554,7 +589,7 @@ ok $dom->children('rss')->first->xml,  'XML mode active';
 ok $dom->at('title')->ancestors->first->xml, 'XML mode active';
 
 # Namespace
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <?xml version="1.0"?>
 <bk:book xmlns='uri:default-ns'
          xmlns:bk='uri:book-ns'
@@ -616,7 +651,7 @@ is $dom->at('[xml\.s]'), undef, 'no result';
 is $dom->at('b\.z'),     undef, 'no result';
 
 # Yadis
-$dom = Mojo::DOM->new->parse(<<'EOF');
+$dom = Mojo::DOM->new(<<'EOF');
 <?xml version="1.0" encoding="UTF-8"?>
 <XRDS xmlns="xri://$xrds">
   <XRD xmlns="xri://$xrd*($v*2.0)">
@@ -662,7 +697,7 @@ my $yadis = <<'EOF';
   </XRD>
 </xrds:XRDS>
 EOF
-$dom = Mojo::DOM->new->parse($yadis);
+$dom = Mojo::DOM->new($yadis);
 ok $dom->xml, 'XML mode detected';
 is $dom->at('XRDS')->namespace, 'xri://$xrds',         'right namespace';
 is $dom->at('XRD')->namespace,  'xri://$xrd*($v*2.0)', 'right namespace';
@@ -697,27 +732,26 @@ is $dom->find('xrds\00003A Service > Type')->[1], undef, 'no result';
 is "$dom", $yadis, 'successful roundtrip';
 
 # Result and iterator order
-$dom = Mojo::DOM->new->parse('<a><b>1</b></a><b>2</b><b>3</b>');
+$dom = Mojo::DOM->new('<a><b>1</b></a><b>2</b><b>3</b>');
 my @numbers;
 $dom->find('b')->each(sub { push @numbers, pop, shift->text });
 is_deeply \@numbers, [1, 1, 2, 2, 3, 3], 'right order';
 
 # Attributes on multiple lines
-$dom = Mojo::DOM->new->parse("<div test=23 id='a' \n class='x' foo=bar />");
+$dom = Mojo::DOM->new("<div test=23 id='a' \n class='x' foo=bar />");
 is $dom->at('div.x')->attr('test'),        23,  'right attribute';
 is $dom->at('[foo="bar"]')->attr('class'), 'x', 'right attribute';
 is $dom->at('div')->attr(baz => undef)->root->to_string,
   '<div baz class="x" foo="bar" id="a" test="23"></div>', 'right result';
 
 # Markup characters in attribute values
-$dom = Mojo::DOM->new->parse(
-  qq{<div id="<a>" \n test='='>Test<div id='><' /></div>});
+$dom = Mojo::DOM->new(qq{<div id="<a>" \n test='='>Test<div id='><' /></div>});
 is $dom->at('div[id="<a>"]')->attr->{test}, '=', 'right attribute';
 is $dom->at('[id="<a>"]')->text, 'Test', 'right text';
 is $dom->at('[id="><"]')->attr->{id}, '><', 'right attribute';
 
 # Empty attributes
-$dom = Mojo::DOM->new->parse(qq{<div test="" test2='' />});
+$dom = Mojo::DOM->new(qq{<div test="" test2='' />});
 is $dom->at('div')->attr->{test},  '', 'empty attribute value';
 is $dom->at('div')->attr->{test2}, '', 'empty attribute value';
 is $dom->at('[test]')->type,  'div', 'right type';
@@ -728,14 +762,13 @@ is $dom->at('[test2=""]')->type, 'div', 'right type';
 is $dom->at('[test3=""]'), undef, 'no result';
 
 # Whitespaces before closing bracket
-$dom = Mojo::DOM->new->parse('<div >content</div>');
+$dom = Mojo::DOM->new('<div >content</div>');
 ok $dom->at('div'), 'tag found';
 is $dom->at('div')->text,    'content', 'right text';
 is $dom->at('div')->content, 'content', 'right text';
 
 # Class with hyphen
-$dom
-  = Mojo::DOM->new->parse('<div class="a">A</div><div class="a-1">A1</div>');
+$dom = Mojo::DOM->new('<div class="a">A</div><div class="a-1">A1</div>');
 @div = ();
 $dom->find('.a')->each(sub { push @div, shift->text });
 is_deeply \@div, ['A'], 'found first element only';
@@ -744,24 +777,24 @@ $dom->find('.a-1')->each(sub { push @div, shift->text });
 is_deeply \@div, ['A1'], 'found last element only';
 
 # Defined but false text
-$dom = Mojo::DOM->new->parse(
+$dom = Mojo::DOM->new(
   '<div><div id="a">A</div><div id="b">B</div></div><div id="0">0</div>');
 @div = ();
 $dom->find('div[id]')->each(sub { push @div, shift->text });
 is_deeply \@div, [qw(A B 0)], 'found all div elements with id';
 
 # Empty tags
-$dom = Mojo::DOM->new->parse('<hr /><br/><br id="br"/><br />');
+$dom = Mojo::DOM->new('<hr /><br/><br id="br"/><br />');
 is "$dom", '<hr><br><br id="br"><br>', 'right result';
 is $dom->at('br')->content, '', 'empty result';
 
 # Inner XML
-$dom = Mojo::DOM->new->parse('<a>xxx<x>x</x>xxx</a>');
+$dom = Mojo::DOM->new('<a>xxx<x>x</x>xxx</a>');
 is $dom->at('a')->content, 'xxx<x>x</x>xxx', 'right result';
 is $dom->content, '<a>xxx<x>x</x>xxx</a>', 'right result';
 
 # Multiple selectors
-$dom = Mojo::DOM->new->parse(
+$dom = Mojo::DOM->new(
   '<div id="a">A</div><div id="b">B</div><div id="c">C</div>');
 @div = ();
 $dom->find('#a, #c')->each(sub { push @div, shift->text });
@@ -772,7 +805,7 @@ is_deeply \@div, [qw(A B)], 'found all div elements with the right ids';
 @div = ();
 $dom->find('div[id="a"], div[id="c"]')->each(sub { push @div, shift->text });
 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
-$dom = Mojo::DOM->new->parse(
+$dom = Mojo::DOM->new(
   '<div id="☃">A</div><div id="b">B</div><div id="♥x">C</div>');
 @div = ();
 $dom->find('#☃, #♥x')->each(sub { push @div, shift->text });
@@ -786,7 +819,7 @@ $dom->find('div[id="☃"], div[id="♥x"]')
 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
 
 # Multiple attributes
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <div foo="bar" bar="baz">A</div>
 <div foo="bar">B</div>
 <div foo="bar" bar="baz">C</div>
@@ -805,7 +838,7 @@ is $dom->at('[foo="bar"]')->next->previous->text, 'A', 'right text';
 is $dom->at('[foo="bar"]')->next->next->next->next, undef, 'no next sibling';
 
 # Pseudo classes
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <form action="/foo">
     <input type="text" name="user" value="test" />
     <input type="checkbox" checked="checked" name="groovy">
@@ -821,6 +854,8 @@ $dom = Mojo::DOM->new->parse(<<EOF);
     </select>
     <input type="submit" value="Ok!" />
     <input type="checkbox" checked name="I">
+    <p id="content">test 123</p>
+    <p id="no_content"><? test ?><!-- 123 --></p>
 </form>
 EOF
 is $dom->find(':root')->[0]->type,     'form', 'right type';
@@ -849,10 +884,12 @@ is $dom->at('optgroup > *:checked[value="e"]')->text,    'E', 'right text';
 is $dom->find(':checked[value="e"]')->[1], undef, 'no result';
 is $dom->find(':empty')->[0]->attr->{name},      'user', 'right name';
 is $dom->find('input:empty')->[0]->attr->{name}, 'user', 'right name';
-is $dom->at(':empty[type^="ch"]')->attr->{name}, 'groovy', 'right name';
+is $dom->at(':empty[type^="ch"]')->attr->{name}, 'groovy',  'right name';
+is $dom->at('p')->attr->{id},                    'content', 'right attribute';
+is $dom->at('p:empty')->attr->{id}, 'no_content', 'right attribute';
 
 # More pseudo classes
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ul>
     <li>A</li>
     <li>B</li>
@@ -991,7 +1028,7 @@ $dom->find('li:nth-child(n)')->each(sub { push @li, shift->text });
 is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements';
 
 # Even more pseudo classes
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ul>
     <li>A</li>
     <p>B</p>
@@ -1112,7 +1149,7 @@ $dom->find('div div:only-of-type')->each(sub { push @e, shift->text });
 is_deeply \@e, [qw(J K)], 'found only child';
 
 # Sibling combinator
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ul>
     <li>A</li>
     <p>B</p>
@@ -1160,7 +1197,7 @@ is $dom->at('#♥ + *:nth-last-child(2)')->text,        'F', 'right text';
 is $dom->at('#♥ ~ *:nth-last-child(2)')->text,        'F', 'right text';
 
 # Adding nodes
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ul>
     <li>A</li>
     <p>B</p>
@@ -1238,7 +1275,7 @@ is "$dom", <<EOF, 'right result';
 EOF
 
 # Optional "head" and "body" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <title>foo</title>
@@ -1248,7 +1285,7 @@ is $dom->at('html > head > title')->text, 'foo', 'right text';
 is $dom->at('html > body')->text,         'bar', 'right text';
 
 # Optional "li" tag
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ul>
   <li>
     <ol>
@@ -1271,7 +1308,7 @@ is $dom->find('ul > li')->[4]->text,           'D', 'right text';
 is $dom->find('ul > li')->[5]->text,           'E', 'right text';
 
 # Optional "p" tag
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <div>
   <p>A</p>
   <P>B
@@ -1294,7 +1331,7 @@ is $dom->at('div > p > img')->attr->{src}, 'foo.png', 'right attribute';
 is $dom->at('div > div')->text, 'X', 'right text';
 
 # Optional "dt" and "dd" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <dl>
   <dt>A</dt>
   <DD>B
@@ -1312,7 +1349,7 @@ is $dom->find('dl > dt')->[2]->text, 'E', 'right text';
 is $dom->find('dl > dd')->[2]->text, 'F', 'right text';
 
 # Optional "rp" and "rt" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <ruby>
   <rp>A</rp>
   <RT>B
@@ -1330,7 +1367,7 @@ is $dom->find('ruby > rp')->[2]->text, 'E', 'right text';
 is $dom->find('ruby > rt')->[2]->text, 'F', 'right text';
 
 # Optional "optgroup" and "option" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <div>
   <optgroup>A
     <option id="foo">B
@@ -1352,7 +1389,7 @@ is $dom->find('div > optgroup')->[2]->text,          'G', 'right text';
 is $dom->find('div > optgroup > option')->[4]->text, 'H', 'right text';
 
 # Optional "colgroup" tag
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
   <col id=morefail>
   <col id=fail>
@@ -1373,7 +1410,7 @@ is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
   'right attribute';
 
 # Optional "thead", "tbody", "tfoot", "tr", "th" and "td" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
   <thead>
     <tr>
@@ -1393,7 +1430,7 @@ is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
 is $dom->at('table > tfoot > tr > td')->text, 'C', 'right text';
 
 # Optional "colgroup", "thead", "tbody", "tr", "th" and "td" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
   <col id=morefail>
   <col id=fail>
@@ -1425,11 +1462,12 @@ is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
   'right attribute';
 is $dom->at('table > thead > tr > th')->text, 'A', 'right text';
 is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text';
-is $dom->at('table > tbody > tr > td')->text,   'B',    'right text';
-is $dom->find('table > tbody > tr > td')->text, "B\nE", 'right text';
+is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
+is $dom->find('table > tbody > tr > td')->map('text')->join("\n"), "B\nE",
+  'right text';
 
 # Optional "colgroup", "tbody", "tr", "th" and "td" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
   <colgroup>
     <col id=foo />
@@ -1451,7 +1489,7 @@ is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
 is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
 
 # Optional "tr" and "td" tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <table>
     <tr>
       <td>A
@@ -1469,7 +1507,7 @@ is $dom->find('table > tr > td')->[2]->text, 'C', 'right text';
 is $dom->find('table > tr > td')->[3]->text, 'D', 'right text';
 
 # Real world table
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <title>Real World!</title>
@@ -1505,13 +1543,14 @@ is $dom->find('tbody > tr > .gamma')->[0]->text, '',            'no text';
 is $dom->find('tbody > tr > .gamma > a')->[0]->text, 'Gamma',     'right text';
 is $dom->find('tbody > tr > .alpha')->[1]->text,     'Alpha Two', 'right text';
 is $dom->find('tbody > tr > .gamma > a')->[1]->text, 'Gamma Two', 'right text';
-my @siblings = $dom->find('tr > td:nth-child(1)')->siblings(':nth-child(even)')
-  ->flatten->all_text->each;
-is_deeply \@siblings, ['Beta', 'Delta', 'Beta Two', 'Delta Two'],
+my @following
+  = $dom->find('tr > td:nth-child(1)')->map(following => ':nth-child(even)')
+  ->flatten->map('all_text')->each;
+is_deeply \@following, ['Beta', 'Delta', 'Beta Two', 'Delta Two'],
   'right results';
 
 # Real world list
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <title>Real World!</title>
@@ -1549,7 +1588,7 @@ is $dom->find('body > ul > li')->[2]->all_text,     'Test 3 2 1', 'right text';
 is $dom->find('body > ul > li > p')->[2]->all_text, '',           'no text';
 
 # Advanced whitespace trimming (punctuation)
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <title>Real World!</title>
@@ -1568,7 +1607,7 @@ is $dom->find('body > div')->[2]->all_text, 'foo: bar baz? yada!',
 is $dom->find('body > div')->[2]->text, 'foo baz!', 'right text';
 
 # Real world JavaScript and CSS
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head>
     <style test=works>#style { foo: style('<test>'); }</style>
@@ -1589,7 +1628,7 @@ is $dom->find('html > head > script')->[1]->text,
   "if (b > c) { alert('&<ohoh>') }", 'right text';
 
 # More real world JavaScript
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <!DOCTYPE html>
 <html>
   <head>
@@ -1612,7 +1651,7 @@ is $dom->find('html > head > script')->[2]->text, '', 'no text';
 is $dom->at('html > body')->text, 'Bar', 'right text';
 
 # Even more real world JavaScript
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <!DOCTYPE html>
 <html>
   <head>
@@ -1635,7 +1674,7 @@ is $dom->find('html > head > script')->[2]->text, '', 'no text';
 is $dom->at('html > body')->text, 'Bar', 'right text';
 
 # Inline DTD
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <?xml version="1.0"?>
 <!-- This is a Test! -->
 <!DOCTYPE root [
@@ -1653,7 +1692,7 @@ is $dom->tree->[5][1], ' root [
   <!ATTLIST root att CDATA #REQUIRED>
 ]', 'right doctype';
 is $dom->at('root')->text, '<hello>world</hello>', 'right text';
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <!doctype book
 SYSTEM "usr.dtd"
 [
@@ -1668,7 +1707,7 @@ SYSTEM "usr.dtd"
 ]', 'right doctype';
 ok !$dom->xml, 'XML mode not detected';
 is $dom->at('foo'), '<foo></foo>', 'right element';
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <?xml version="1.0" encoding = 'utf-8'?>
 <!DOCTYPE foo [
   <!ELEMENT foo ANY>
@@ -1687,7 +1726,7 @@ is $dom->tree->[3][1], ' foo [
 ]  ', 'right doctype';
 is $dom->at('foo')->attr->{'xml:lang'}, 'de', 'right attribute';
 is $dom->at('foo')->text, 'Check!', 'right text';
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <!DOCTYPE TESTSUITE PUBLIC "my.dtd" 'mhhh' [
   <!ELEMENT foo ANY>
   <!ATTLIST foo bar ENTITY 'true'>
@@ -1710,7 +1749,7 @@ is $dom->tree->[1][1], ' TESTSUITE PUBLIC "my.dtd" \'mhhh\' [
 is $dom->at('foo')->attr('bar'), 'false', 'right attribute';
 
 # Broken "font" block and useless end tags
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head><title>Test</title></head>
   <body>
@@ -1725,7 +1764,7 @@ is $dom->at('html > head > title')->text,          'Test', 'right text';
 is $dom->at('html body table tr td > font')->text, 'test', 'right text';
 
 # Different broken "font" block
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head><title>Test</title></head>
   <body>
@@ -1745,7 +1784,7 @@ is $dom->find('html > body > font > table > tr > td')->[1]->text, 'test2',
   'right text';
 
 # Broken "font" and "div" blocks
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head><title>Test</title></head>
   <body>
@@ -1761,7 +1800,7 @@ is $dom->at('html body font > div')->text,       'test1', 'right text';
 is $dom->at('html body font > div > div')->text, 'test2', 'right text';
 
 # Broken "div" blocks
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head><title>Test</title></head>
   <body>
@@ -1777,7 +1816,7 @@ is $dom->at('html head title')->text,                 'Test', 'right text';
 is $dom->at('html body div table tr td > div')->text, 'test', 'right text';
 
 # And another broken "font" block
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <html>
   <head><title>Test</title></head>
   <body>
@@ -1820,7 +1859,7 @@ is $dom, <<EOF, 'right result';
 EOF
 
 # A collection of wonderful screwups
-$dom = Mojo::DOM->new->parse(<<'EOF');
+$dom = Mojo::DOM->new(<<'EOF');
 <!DOCTYPE html>
 <html lang="en">
   <head><title>Wonderful Screwups</title></head>
@@ -1851,7 +1890,7 @@ $dom = Mojo::DOM->new('<br< abc abc abc abc abc abc abc abc<p>Test</p>');
 is $dom->at('p')->text, 'Test', 'right text';
 
 # Modifying an XML document
-$dom = Mojo::DOM->new->parse(<<'EOF');
+$dom = Mojo::DOM->new(<<'EOF');
 <?xml version='1.0' encoding='UTF-8'?>
 <XMLTest />
 EOF
@@ -1877,7 +1916,7 @@ is $dom->at('div:root')->text, 'Test', 'right text';
 
 # Ensure XML semantics
 ok !!Mojo::DOM->new->xml(1)->parse('<foo />')->xml, 'XML mode active';
-$dom = Mojo::DOM->new->parse(<<'EOF');
+$dom = Mojo::DOM->new(<<'EOF');
 <?xml version='1.0' encoding='UTF-8'?>
 <script>
   <table>
@@ -1913,7 +1952,7 @@ is $dom->find('table > td > tr > thead')->[2], undef, 'no result';
 is $dom->find('table > td > tr > thead')->size, 2, 'right number of elements';
 
 # Nested tables
-$dom = Mojo::DOM->new->parse(<<'EOF');
+$dom = Mojo::DOM->new(<<'EOF');
 <table id="foo">
   <tr>
     <td>
@@ -1963,47 +2002,9 @@ $dom->find('b')->each(
   }
 );
 is_deeply \@results, [qw(baz yada)], 'right results';
-
-# AUTOLOAD children in XML mode
-$dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
-<a id="one">
-  <B class="two" test>
-    foo
-    <c id="three">bar</c>
-    <c ID="four">baz</c>
-  </B>
-</a>
-EOF
-ok $dom->xml, 'XML mode active';
-is $dom->a->B->text, 'foo', 'right text';
-is $dom->a->B->c->[0]->text, 'bar', 'right text';
-is $dom->a->B->c->[1]->text, 'baz', 'right text';
-is $dom->a->B->c->[2], undef, 'no result';
-is $dom->a->B->c->size, 2, 'right number of elements';
-
-# AUTOLOAD children in HTML mode
-$dom = Mojo::DOM->new(<<EOF);
-<a id="one">
-  <B class="two" test>
-    foo
-    <c id="three">bar</c>
-    <c ID="four">baz</c>
-  </B>
-</a>
-EOF
-ok !$dom->xml, 'XML mode not active';
-is $dom->a->b->text, 'foo', 'right text';
-is $dom->a->b->c->[0]->text, 'bar', 'right text';
-is $dom->a->b->c->[1]->text, 'baz', 'right text';
-is $dom->a->b->c->[2], undef, 'no result';
-is $dom->a->b->c->size, 2, 'right number of elements';
-
-# Missing method and function (AUTOLOAD)
-eval { Mojo::DOM->new->missing };
-like $@, qr/^Can't locate object method "missing" via package "Mojo::DOM"/,
-  'right error';
-eval { Mojo::DOM::missing() };
-like $@, qr/^Undefined subroutine &Mojo::DOM::missing called/, 'right error';
+is $dom->at('b')->at('a')->text, 'bar', 'right text';
+is $dom->at('c > b > a')->text, 'bar', 'right text';
+is $dom->at('b')->at('c > b > a'), undef, 'no result';
 
 # Direct hash access to attributes in XML mode
 $dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
@@ -2016,30 +2017,26 @@ $dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
 </a>
 EOF
 ok $dom->xml, 'XML mode active';
-is $dom->a->{id}, 'one', 'right attribute';
-is_deeply [sort keys %{$dom->a}], ['id'], 'right attributes';
-is $dom->a->B->text, 'foo', 'right text';
-is $dom->at('a')->B->text, 'foo', 'right text';
-is $dom->find('a')->[0]->B->text, 'foo', 'right text';
-is $dom->a->B->{class}, 'two', 'right attribute';
-is $dom->at('a')->B->{class}, 'two', 'right attribute';
-is $dom->find('a')->[0]->B->{class}, 'two', 'right attribute';
-is_deeply [sort keys %{$dom->a->B}], [qw(class test)], 'right attributes';
-is $dom->a->B->c->[0]->text, 'bar', 'right text';
-is $dom->a->B->c->[0]{id}, 'three', 'right attribute';
-is_deeply [sort keys %{$dom->a->B->c->[0]}], ['id'], 'right attributes';
-is $dom->a->B->c->[1]->text, 'baz', 'right text';
-is $dom->a->B->c->[1]{ID}, 'four', 'right attribute';
-is_deeply [sort keys %{$dom->a->B->c->[1]}], ['ID'], 'right attributes';
-is $dom->a->B->c->[2], undef, 'no result';
-is $dom->a->B->c->size, 2, 'right number of elements';
+is $dom->at('a')->{id}, 'one', 'right attribute';
+is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
+is $dom->at('a')->at('B')->text, 'foo', 'right text';
+is $dom->at('B')->{class}, 'two', 'right attribute';
+is_deeply [sort keys %{$dom->at('a B')}], [qw(class test)], 'right attributes';
+is $dom->find('a B c')->[0]->text, 'bar', 'right text';
+is $dom->find('a B c')->[0]{id}, 'three', 'right attribute';
+is_deeply [sort keys %{$dom->find('a B c')->[0]}], ['id'], 'right attributes';
+is $dom->find('a B c')->[1]->text, 'baz', 'right text';
+is $dom->find('a B c')->[1]{ID}, 'four', 'right attribute';
+is_deeply [sort keys %{$dom->find('a B c')->[1]}], ['ID'], 'right attributes';
+is $dom->find('a B c')->[2], undef, 'no result';
+is $dom->find('a B c')->size, 2, 'right number of elements';
 @results = ();
-$dom->a->B->c->each(sub { push @results, $_->text });
+$dom->find('a B c')->each(sub { push @results, $_->text });
 is_deeply \@results, [qw(bar baz)], 'right results';
-is $dom->a->B->c, qq{<c id="three">bar</c>\n<c ID="four">baz</c>},
-  'right result';
+is $dom->find('a B c')->join("\n"),
+  qq{<c id="three">bar</c>\n<c ID="four">baz</c>}, 'right result';
 is_deeply [keys %$dom], [], 'root has no attributes';
-is $dom->find('#nothing'), '', 'no result';
+is $dom->find('#nothing')->join, '', 'no result';
 
 # Direct hash access to attributes in HTML mode
 $dom = Mojo::DOM->new(<<EOF);
@@ -2052,30 +2049,26 @@ $dom = Mojo::DOM->new(<<EOF);
 </a>
 EOF
 ok !$dom->xml, 'XML mode not active';
-is $dom->a->{id}, 'one', 'right attribute';
-is_deeply [sort keys %{$dom->a}], ['id'], 'right attributes';
-is $dom->a->b->text, 'foo', 'right text';
-is $dom->at('a')->b->text, 'foo', 'right text';
-is $dom->find('a')->[0]->b->text, 'foo', 'right text';
-is $dom->a->b->{class}, 'two', 'right attribute';
-is $dom->at('a')->b->{class}, 'two', 'right attribute';
-is $dom->find('a')->[0]->b->{class}, 'two', 'right attribute';
-is_deeply [sort keys %{$dom->a->b}], [qw(class test)], 'right attributes';
-is $dom->a->b->c->[0]->text, 'bar', 'right text';
-is $dom->a->b->c->[0]{id}, 'three', 'right attribute';
-is_deeply [sort keys %{$dom->a->b->c->[0]}], ['id'], 'right attributes';
-is $dom->a->b->c->[1]->text, 'baz', 'right text';
-is $dom->a->b->c->[1]{id}, 'four', 'right attribute';
-is_deeply [sort keys %{$dom->a->b->c->[1]}], ['id'], 'right attributes';
-is $dom->a->b->c->[2], undef, 'no result';
-is $dom->a->b->c->size, 2, 'right number of elements';
+is $dom->at('a')->{id}, 'one', 'right attribute';
+is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
+is $dom->at('a')->at('b')->text, 'foo', 'right text';
+is $dom->at('b')->{class}, 'two', 'right attribute';
+is_deeply [sort keys %{$dom->at('a b')}], [qw(class test)], 'right attributes';
+is $dom->find('a b c')->[0]->text, 'bar', 'right text';
+is $dom->find('a b c')->[0]{id}, 'three', 'right attribute';
+is_deeply [sort keys %{$dom->find('a b c')->[0]}], ['id'], 'right attributes';
+is $dom->find('a b c')->[1]->text, 'baz', 'right text';
+is $dom->find('a b c')->[1]{id}, 'four', 'right attribute';
+is_deeply [sort keys %{$dom->find('a b c')->[1]}], ['id'], 'right attributes';
+is $dom->find('a b c')->[2], undef, 'no result';
+is $dom->find('a b c')->size, 2, 'right number of elements';
 @results = ();
-$dom->a->b->c->each(sub { push @results, $_->text });
+$dom->find('a b c')->each(sub { push @results, $_->text });
 is_deeply \@results, [qw(bar baz)], 'right results';
-is $dom->a->b->c, qq{<c id="three">bar</c>\n<c id="four">baz</c>},
-  'right result';
+is $dom->find('a b c')->join("\n"),
+  qq{<c id="three">bar</c>\n<c id="four">baz</c>}, 'right result';
 is_deeply [keys %$dom], [], 'root has no attributes';
-is $dom->find('#nothing'), '', 'no result';
+is $dom->find('#nothing')->join, '', 'no result';
 
 # Append and prepend content
 $dom = Mojo::DOM->new('<a><b>Test<c /></b></a>');
@@ -2123,10 +2116,10 @@ $dom = Mojo::DOM->new(<<EOF);
   </tr>
 </table>
 EOF
-is $dom->table->tr->td->[0]->div->{id}, 'A', 'right attribute';
-is $dom->table->tr->td->[1]->div->{id}, 'B', 'right attribute';
-is $dom->table->tr->td->[2], undef, 'no result';
-is $dom->table->tr->td->size, 2, 'right number of elements';
+is $dom->find('table tr td')->[0]->at('div')->{id}, 'A', 'right attribute';
+is $dom->find('table tr td')->[1]->at('div')->{id}, 'B', 'right attribute';
+is $dom->find('table tr td')->[2], undef, 'no result';
+is $dom->find('table tr td')->size, 2, 'right number of elements';
 is "$dom", <<EOF, 'right result';
 <table>
   <tr>
@@ -2152,19 +2145,22 @@ is $dom->text(0), "\n", 'right text';
 is $dom->all_text, "looks like\n  it\n    really\n  works", 'right text';
 is $dom->all_text(0), "\n  looks\n  like\n  it\n    really\n  \n  works\n\n",
   'right text';
-is $dom->div->text, 'looks works', 'right text';
-is $dom->div->text(0), "\n  looks\n  \n  works\n", 'right text';
-is $dom->div->all_text, "looks like\n  it\n    really\n  works", 'right text';
-is $dom->div->all_text(0),
+is $dom->at('div')->text, 'looks works', 'right text';
+is $dom->at('div')->text(0), "\n  looks\n  \n  works\n", 'right text';
+is $dom->at('div')->all_text, "looks like\n  it\n    really\n  works",
+  'right text';
+is $dom->at('div')->all_text(0),
   "\n  looks\n  like\n  it\n    really\n  \n  works\n", 'right text';
-is $dom->div->pre->text, "\n  ", 'right text';
-is $dom->div->pre->text(0), "\n  ", 'right text';
-is $dom->div->pre->all_text, "like\n  it\n    really\n  ", 'right text';
-is $dom->div->pre->all_text(0), "like\n  it\n    really\n  ", 'right text';
-is $dom->div->pre->code->text, "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->text(0), "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->all_text, "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->all_text(0), "like\n  it\n    really", 'right text';
+is $dom->at('div pre')->text, "\n  ", 'right text';
+is $dom->at('div pre')->text(0), "\n  ", 'right text';
+is $dom->at('div pre')->all_text, "like\n  it\n    really\n  ", 'right text';
+is $dom->at('div pre')->all_text(0), "like\n  it\n    really\n  ",
+  'right text';
+is $dom->at('div pre code')->text, "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->text(0), "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->all_text, "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->all_text(0), "like\n  it\n    really",
+  'right text';
 
 # PoCo example with whitespace sensitive text
 $dom = Mojo::DOM->new(<<EOF);
@@ -2190,21 +2186,23 @@ Springfield, VT 12345 USA</formatted>
   </entry>
 </response>
 EOF
-is $dom->find('entry')->[0]->displayName->text, 'Homer Simpson', 'right text';
-is $dom->find('entry')->[0]->id->text,          '1286823',       'right text';
-is $dom->find('entry')->[0]->addresses->children('type')->[0]->text, 'home',
+is $dom->find('entry')->[0]->at('displayName')->text, 'Homer Simpson',
   'right text';
-is $dom->find('entry')->[0]->addresses->formatted->text,
+is $dom->find('entry')->[0]->at('id')->text, '1286823', 'right text';
+is $dom->find('entry')->[0]->at('addresses')->children('type')->[0]->text,
+  'home', 'right text';
+is $dom->find('entry')->[0]->at('addresses formatted')->text,
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
-is $dom->find('entry')->[0]->addresses->formatted->text(0),
+is $dom->find('entry')->[0]->at('addresses formatted')->text(0),
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
-is $dom->find('entry')->[1]->displayName->text, 'Marge Simpson', 'right text';
-is $dom->find('entry')->[1]->id->text,          '1286822',       'right text';
-is $dom->find('entry')->[1]->addresses->children('type')->[0]->text, 'home',
+is $dom->find('entry')->[1]->at('displayName')->text, 'Marge Simpson',
   'right text';
-is $dom->find('entry')->[1]->addresses->formatted->text,
+is $dom->find('entry')->[1]->at('id')->text, '1286822', 'right text';
+is $dom->find('entry')->[1]->at('addresses')->children('type')->[0]->text,
+  'home', 'right text';
+is $dom->find('entry')->[1]->at('addresses formatted')->text,
   '742 Evergreen Terrace Springfield, VT 12345 USA', 'right text';
-is $dom->find('entry')->[1]->addresses->formatted->text(0),
+is $dom->find('entry')->[1]->at('addresses formatted')->text(0),
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
 is $dom->find('entry')->[2], undef, 'no result';
 is $dom->find('entry')->size, 2, 'right number of elements';
@@ -2258,7 +2256,7 @@ is $dom->find('a[accesskey*=1]')->[1], undef, 'no result';
 is $dom->at('a[accesskey*="."]'), undef, 'no result';
 
 # Empty attribute value
-$dom = Mojo::DOM->new->parse(<<EOF);
+$dom = Mojo::DOM->new(<<EOF);
 <foo bar=>
   test
 </foo>
@@ -2281,6 +2279,22 @@ is "$dom", <<EOF, 'right result';
 <bar>after</bar>
 EOF
 
+# Nested description lists
+$dom = Mojo::DOM->new(<<EOF);
+<dl>
+  <dt>A</dt>
+  <DD>
+    <dl>
+      <dt>B
+      <dd>C
+    </dl>
+  </dd>
+</dl>
+EOF
+is $dom->find('dl > dd > dl > dt')->[0]->text, 'B', 'right text';
+is $dom->find('dl > dd > dl > dd')->[0]->text, 'C', 'right text';
+is $dom->find('dl > dt')->[0]->text,           'A', 'right text';
+
 # Nested lists
 $dom = Mojo::DOM->new(<<EOF);
 <div>
@@ -2309,6 +2323,13 @@ 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';
@@ -2327,9 +2348,9 @@ is $dom->parse('<![CDATA[0]]>'), '<![CDATA[0]]>', 'successful roundtrip';
 is $dom->parse('<?0?>'),         '<?0?>',         'successful roundtrip';
 
 # Not self-closing
-$dom = Mojo::DOM->new('<div /><div><pre />test</div>');
+$dom = Mojo::DOM->new('<div />< div ><pre />test</div >123');
 is $dom->at('div > div > pre')->text, 'test', 'right text';
-is "$dom", '<div><div><pre>test</pre></div></div>', 'right result';
+is "$dom", '<div><div><pre>test</pre></div>123</div>', 'right result';
 $dom = Mojo::DOM->new('<p /><svg><circle /><circle /></svg>');
 is $dom->find('p > svg > circle')->size, 2, 'two circles';
 is "$dom", '<p><svg><circle></circle><circle></circle></svg></p>',
@@ -16,41 +16,21 @@ eval { $e->emit('die') };
 is $@, "works!\n", 'right error';
 
 # Unhandled error event
-eval { $e->emit(error => 'just') };
-like $@, qr/^Mojo::EventEmitter: just/, 'right error';
-eval { $e->emit_safe(error => 'works') };
+eval { $e->emit(error => 'works') };
 like $@, qr/^Mojo::EventEmitter: works/, 'right error';
 
+# Catch
+my $err;
+ok !$e->has_subscribers('foo'), 'no subscribers';
+$e->catch(sub { $err = pop });
+ok $e->has_subscribers('error'), 'has subscribers';
+$e->emit(error => 'just works!');
+is $err, 'just works!', 'right error';
+
 # Exception in error event
 $e->once(error => sub { die "$_[1]entional" });
 eval { $e->emit(error => 'int') };
 like $@, qr/^intentional/, 'right error';
-$e->once(error => sub { die "$_[1]entional" });
-eval { $e->emit_safe(error => 'int') };
-like $@, qr/^Mojo::EventEmitter: Event "error" failed: intentional/,
-  'right error';
-
-# Error fallback
-my ($echo, $err);
-$e->catch(sub { $err = pop })->on(test2 => sub { $echo .= 'echo: ' . pop });
-$e->on(
-  test2 => sub {
-    my ($e, $msg) = @_;
-    die "test2: $msg\n";
-  }
-);
-my $cb = sub { $echo .= 'echo2: ' . pop };
-$e->on(test2 => $cb);
-$e->emit_safe('test2', 'works!');
-is $echo, 'echo: works!echo2: works!', 'right echo';
-is $err, qq{Event "test2" failed: test2: works!\n}, 'right error';
-($echo, $err) = ();
-is scalar @{$e->subscribers('test2')}, 3, 'three subscribers';
-$e->unsubscribe(test2 => $cb);
-is scalar @{$e->subscribers('test2')}, 2, 'two subscribers';
-$e->emit_safe('test2', 'works!');
-is $echo, 'echo: works!', 'right echo';
-is $err, qq{Event "test2" failed: test2: works!\n}, 'right error';
 
 # Normal event again
 $e->emit('test1');
@@ -126,7 +106,7 @@ is $once, 1, 'event was not emitted again';
 # Unsubscribe
 $e = Mojo::EventEmitter->new;
 my $counter;
-$cb = $e->on(foo => sub { $counter++ });
+my $cb = $e->on(foo => sub { $counter++ });
 $e->on(foo => sub { $counter++ });
 $e->on(foo => sub { $counter++ });
 $e->unsubscribe(foo => $e->once(foo => sub { $counter++ }));
@@ -151,7 +131,7 @@ is $buffer, '', 'no result';
 $e->emit(one => $buffer => 'two');
 is $buffer, 'abctwo123two', 'right result';
 $e->once(one => sub { $_[1] .= 'def' });
-$e->emit_safe(one => $buffer => 'three');
+$e->emit(one => $buffer => 'three');
 is $buffer, 'abctwo123twoabcthree123threedef', 'right result';
 $e->emit(one => $buffer => 'x');
 is $buffer, 'abctwo123twoabcthree123threedefabcx123x', 'right result';
@@ -50,14 +50,18 @@ is $headers->accept_charset('foo')->accept_charset,   'foo', 'right value';
 is $headers->accept_encoding('foo')->accept_encoding, 'foo', 'right value';
 is $headers->accept_language('foo')->accept_language, 'foo', 'right value';
 is $headers->accept_ranges('foo')->accept_ranges,     'foo', 'right value';
-is $headers->allow('foo')->allow,                     'foo', 'right value';
-is $headers->authorization('foo')->authorization,     'foo', 'right value';
-is $headers->connection('foo')->connection,           'foo', 'right value';
-is $headers->cache_control('foo')->cache_control,     'foo', 'right value';
+is $headers->access_control_allow_origin('foo')->access_control_allow_origin,
+  'foo', 'right value';
+is $headers->allow('foo')->allow,                 'foo', 'right value';
+is $headers->authorization('foo')->authorization, 'foo', 'right value';
+is $headers->connection('foo')->connection,       'foo', 'right value';
+is $headers->cache_control('foo')->cache_control, 'foo', 'right value';
 is $headers->content_disposition('foo')->content_disposition, 'foo',
   'right value';
 is $headers->content_encoding('foo')->content_encoding,   'foo', 'right value';
+is $headers->content_language('foo')->content_language,   'foo', 'right value';
 is $headers->content_length('foo')->content_length,       'foo', 'right value';
+is $headers->content_location('foo')->content_location,   'foo', 'right value';
 is $headers->content_range('foo')->content_range,         'foo', 'right value';
 is $headers->content_type('foo')->content_type,           'foo', 'right value';
 is $headers->cookie('foo')->cookie,                       'foo', 'right value';
@@ -86,9 +90,11 @@ is $headers->sec_websocket_protocol('foo')->sec_websocket_protocol, 'foo',
   'right value';
 is $headers->sec_websocket_version('foo')->sec_websocket_version, 'foo',
   'right value';
-is $headers->server('foo')->server,                       'foo', 'right value';
-is $headers->set_cookie('foo')->set_cookie,               'foo', 'right value';
-is $headers->status('foo')->status,                       'foo', 'right value';
+is $headers->server('foo')->server,         'foo', 'right value';
+is $headers->set_cookie('foo')->set_cookie, 'foo', 'right value';
+is $headers->status('foo')->status,         'foo', 'right value';
+is $headers->strict_transport_security('foo')->strict_transport_security,
+  'foo', 'right value';
 is $headers->te('foo')->te,                               'foo', 'right value';
 is $headers->trailer('foo')->trailer,                     'foo', 'right value';
 is $headers->transfer_encoding('foo')->transfer_encoding, 'foo', 'right value';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -85,10 +82,10 @@ $id = Mojo::IOLoop->server(
     Mojo::IOLoop->stop;
   }
 );
-my $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($id)->port;
 Mojo::IOLoop->acceptor($id)->on(accept => sub { $handle2 = pop });
 $id2
-  = Mojo::IOLoop->client((address => 'localhost', port => $port) => sub { });
+  = Mojo::IOLoop->client((address => '127.0.0.1', port => $port) => sub { });
 Mojo::IOLoop->start;
 $count = 0;
 Mojo::IOLoop->recurring(10 => sub { $timer++ });
@@ -130,7 +127,7 @@ $id = Mojo::IOLoop->server(
     );
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 my $delay = Mojo::IOLoop->delay;
 my $end   = $delay->begin;
 $handle = undef;
@@ -161,7 +158,7 @@ is $buffer, 'acceptedhelloworld', 'right result';
 
 # Removed listen socket
 $id = $loop->server({address => '127.0.0.1'} => sub { });
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 my $connected;
 $loop->client(
   {port => $port} => sub {
@@ -189,7 +186,7 @@ $id    = Mojo::IOLoop->server(
     $stream->on(close => $end);
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 my $end2 = $delay->begin;
 $id = Mojo::IOLoop->client(
   (port => $port) => sub {
@@ -229,7 +226,7 @@ $id = Mojo::IOLoop->server(
     );
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 Mojo::IOLoop->client(
   {port => $port} => sub {
     my ($loop, $err, $stream) = @_;
@@ -259,7 +256,7 @@ is $loop->max_connections, 0, 'right value';
 $err  = '';
 $loop = Mojo::IOLoop->new(max_accepts => 1);
 $id   = $loop->server({address => '127.0.0.1'} => sub { shift; shift->close });
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client({port => $port} => sub { });
 $loop->timer(3 => sub { shift->stop; $err = 'failed' });
 $loop->start;
@@ -7,8 +7,6 @@ use Mojo::IOLoop::Server;
 
 plan skip_all => 'set TEST_IPV6 to enable this test (developer only!)'
   unless $ENV{TEST_IPV6};
-plan skip_all => 'IO::Socket::IP 0.20 required for this test!'
-  unless Mojo::IOLoop::Server::IPV6;
 
 use Mojo::IOLoop;
 
@@ -24,7 +22,7 @@ my $id  = Mojo::IOLoop->server(
     $stream->on(read => sub { $server .= pop });
   }
 );
-my $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($id)->port;
 my $end2 = $delay->begin;
 Mojo::IOLoop->client(
   {address => '[::1]', port => $port} => sub {
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -13,26 +10,24 @@ plan skip_all => 'set TEST_TLS to enable this test (developer only!)'
 plan skip_all => 'IO::Socket::SSL 1.84 required for this test!'
   unless Mojo::IOLoop::Server::TLS;
 
-# To regenerate all required certificates run these commands (18.04.2012)
+# To regenerate all required certificates run these commands (12.12.2014)
 # openssl genrsa -out ca.key 1024
 # openssl req -new -key ca.key -out ca.csr -subj "/C=US/CN=ca"
 # openssl req -x509 -days 7300 -key ca.key -in ca.csr -out ca.crt
 #
 # openssl genrsa -out server.key 1024
-# openssl req -new -key server.key -out server.csr -subj "/C=US/CN=localhost"
+# openssl req -new -key server.key -out server.csr -subj "/C=US/CN=127.0.0.1"
 # openssl x509 -req -days 7300 -in server.csr -out server.crt -CA ca.crt \
 #   -CAkey ca.key -CAcreateserial
 #
 # openssl genrsa -out client.key 1024
-# openssl req -new -key client.key -out client.csr -subj "/C=US/CN=localhost"
+# openssl req -new -key client.key -out client.csr -subj "/C=US/CN=127.0.0.1"
 # openssl x509 -req -days 7300 -in client.csr -out client.crt -CA ca.crt \
 #   -CAkey ca.key -CAcreateserial
 #
-# openssl genrsa -out badclient.key 1024
-# openssl req -new -key badclient.key -out badclient.csr \
-#   -subj "/C=US/CN=badclient"
-# openssl req -x509 -days 7300 -key badclient.key -in badclient.csr \
-#   -out badclient.crt
+# openssl genrsa -out bad.key 1024
+# openssl req -new -key bad.key -out bad.csr -subj "/C=US/CN=bad"
+# openssl req -x509 -days 7300 -key bad.key -in bad.csr -out bad.crt
 use Mojo::IOLoop;
 
 # Built-in certificate
@@ -48,7 +43,7 @@ my $id  = $loop->server(
     $stream->on(read => sub { $server .= pop });
   }
 );
-my $port = $loop->acceptor($id)->handle->sockport;
+my $port = $loop->acceptor($id)->port;
 my $end2 = $delay->begin;
 $loop->client(
   {port => $port, tls => 1} => sub {
@@ -91,7 +86,7 @@ $id  = Mojo::IOLoop->server(
     $stream->timeout(0.5);
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 $end2 = $delay->begin;
 Mojo::IOLoop->client(
   port     => $port,
@@ -125,8 +120,8 @@ my $client_err;
 Mojo::IOLoop->client(
   port     => $port,
   tls      => 1,
-  tls_cert => 't/mojo/certs/badclient.crt',
-  tls_key  => 't/mojo/certs/badclient.key',
+  tls_cert => 't/mojo/certs/bad.crt',
+  tls_key  => 't/mojo/certs/bad.key',
   sub {
     shift->stop;
     $client_err = shift;
@@ -158,7 +153,7 @@ $id = $loop->server(
   tls_key  => 't/mojo/certs/server.key',
   sub { $server_err = 'accepted' }
 );
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client(
   port     => $port,
   tls      => 1,
@@ -198,7 +193,7 @@ $id  = Mojo::IOLoop->server(
     $stream->on(read => sub { $server .= pop });
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 $end2 = $delay->begin;
 Mojo::IOLoop->client(
   port     => $port,
@@ -235,11 +230,11 @@ $loop = Mojo::IOLoop->new;
 $id = $loop->server(
   address  => '127.0.0.1',
   tls      => 1,
-  tls_cert => 't/mojo/certs/badclient.crt',
-  tls_key  => 't/mojo/certs/badclient.key',
+  tls_cert => 't/mojo/certs/bad.crt',
+  tls_key  => 't/mojo/certs/bad.key',
   sub { $server_err = 'accepted' }
 );
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client(
   port   => $port,
   tls    => 1,
@@ -259,11 +254,11 @@ $loop = Mojo::IOLoop->new;
 $id = $loop->server(
   address  => '127.0.0.1',
   tls      => 1,
-  tls_cert => 't/mojo/certs/server.crt',
-  tls_key  => 't/mojo/certs/server.key',
+  tls_cert => 't/mojo/certs/bad.crt',
+  tls_key  => 't/mojo/certs/bad.key',
   sub { $server_err = 'accepted' }
 );
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client(
   address => '127.0.0.1',
   port    => $port,
@@ -284,11 +279,11 @@ $loop = Mojo::IOLoop->new;
 $id = $loop->server(
   address  => '127.0.0.1',
   tls      => 1,
-  tls_cert => 't/mojo/certs/badclient.crt',
-  tls_key  => 't/mojo/certs/badclient.key',
+  tls_cert => 't/mojo/certs/bad.crt',
+  tls_key  => 't/mojo/certs/bad.key',
   sub { $server_err = 'accepted' }
 );
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client(
   port   => $port,
   tls    => 1,
@@ -320,12 +315,12 @@ $id = $loop->server(
     $server = 'accepted';
   }
 );
-$port = $loop->acceptor($id)->handle->sockport;
+$port = $loop->acceptor($id)->port;
 $loop->client(
   port     => $port,
   tls      => 1,
-  tls_cert => 't/mojo/certs/badclient.crt',
-  tls_key  => 't/mojo/certs/badclient.key',
+  tls_cert => 't/mojo/certs/bad.crt',
+  tls_key  => 't/mojo/certs/bad.key',
   sub {
     my ($loop, $err, $stream) = @_;
     $stream->timeout(0.5);
@@ -351,7 +346,7 @@ $id = Mojo::IOLoop->server(
   tls_verify => 0x01,
   sub { $server = 'accepted' }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 Mojo::IOLoop->client(
   {port => $port, tls => 1} => sub {
     shift->stop;
@@ -10,7 +10,7 @@ use Mojo::Base -strict;
 
 use Test::More;
 use Mojo::ByteStream 'b';
-use Mojo::JSON qw(decode_json encode_json j);
+use Mojo::JSON qw(decode_json encode_json false from_json j to_json true);
 use Mojo::Util 'encode';
 use Scalar::Util 'dualvar';
 
@@ -54,8 +54,7 @@ is_deeply $array, [Mojo::JSON->true], 'decode [true]';
 $array = decode_json '[null]';
 is_deeply $array, [undef], 'decode [null]';
 $array = decode_json '[true, false]';
-is_deeply $array, [Mojo::JSON->true, Mojo::JSON->false],
-  'decode [true, false]';
+is_deeply $array, [true, false], 'decode [true, false]';
 $value = decode_json 'true';
 is $value, Mojo::JSON->true, 'decode true';
 $value = decode_json 'false';
@@ -243,13 +242,18 @@ is_deeply $hash, {foo => 'c:\progra~1\mozill~1\firefox.exe'},
 $bytes = encode_json(['a' x 32768]);
 is_deeply decode_json($bytes), ['a' x 32768], 'successful roundtrip';
 
-# u2028 and u2029
-$bytes = encode_json ["\x{2028}test\x{2029}123"];
-is index($bytes, b("\x{2028}")->encode), -1, 'properly escaped';
-is index($bytes, b("\x{2029}")->encode), -1, 'properly escaped';
-is_deeply decode_json($bytes), ["\x{2028}test\x{2029}123"],
+# u2028, u2029 and slash
+$bytes = encode_json ["\x{2028}test\x{2029}123</script>"];
+is $bytes, '["\u2028test\u2029123<\/script>"]',
+  'escaped u2028, u2029 and slash';
+is_deeply decode_json($bytes), ["\x{2028}test\x{2029}123</script>"],
   'successful roundtrip';
 
+# JSON without UTF-8 encoding
+is_deeply from_json('["♥"]'), ['♥'], 'characters decoded';
+is to_json(['♥']), '["♥"]', 'characters encoded';
+is_deeply from_json(to_json(["\xe9"])), ["\xe9"], 'successful roundtrip';
+
 # Blessed reference
 $bytes = encode_json [b('test')];
 is_deeply decode_json($bytes), ['test'], 'successful roundtrip';
@@ -275,9 +279,13 @@ is encode_json({false => \!!$bytes}), '{"false":false}',
 is encode_json({false => \$bytes}), '{"false":false}',
   'encode false boolean from reference';
 
-# Stringify booleans
-is(Mojo::JSON->true,  1, 'right value');
-is(Mojo::JSON->false, 0, 'right value');
+# Booleans in different contexts
+ok true, 'true';
+is true, 1, 'right string value';
+is true + 0, 1, 'right numeric value';
+ok !false, 'false';
+is false, 0, 'right string value';
+is false + 0, 0, 'right numeric value';
 
 # Upgraded numbers
 my $num = 3;
@@ -317,67 +325,74 @@ like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/,
   'encode "nan" as string';
 
 # "null"
-my $json = Mojo::JSON->new;
-is $json->decode('null'), undef, 'decode null';
-ok !$json->error, 'no error';
 is j('null'), undef, 'decode null';
 
 # Errors
-is $json->decode('test'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string, array, object, number,'
-  . ' boolean or null at line 0, offset 0', 'right error';
-is $json->decode('["♥"]'), undef, 'wide character in input';
-is $json->error, 'Wide character in input', 'right error';
-is $json->decode(b('["\\ud800"]')->encode), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Missing low-surrogate at line 1, offset 8',
+eval { decode_json 'test' };
+like $@, qr/Malformed JSON: Expected string, array, object/, 'right error';
+like $@, qr/object, number, boolean or null at line 0, offset 0/,
+  'right error';
+eval { decode_json b('["\\ud800"]')->encode };
+like $@, qr/Malformed JSON: Missing low-surrogate at line 1, offset 8/,
+  'right error';
+eval { decode_json b('["\\udf46"]')->encode };
+like $@, qr/Malformed JSON: Missing high-surrogate at line 1, offset 8/,
+  'right error';
+eval { decode_json '[[]' };
+like $@, qr/Malformed JSON: Expected comma or right square bracket/,
+  'right error';
+like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error';
+eval { decode_json '{{}' };
+like $@,
+  qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/,
+  'right error';
+eval { decode_json "[\"foo\x00]" };
+like $@, qr/Malformed JSON: Unexpected character or invalid escape/,
   'right error';
-is $json->decode(b('["\\udf46"]')->encode), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Missing high-surrogate at line 1, offset 8',
+like $@, qr/escape while parsing string at line 1, offset 5/, 'right error';
+eval { decode_json '{"foo":"bar"{' };
+like $@, qr/Malformed JSON: Expected comma or right curly bracket/,
   'right error';
-is $json->decode('[[]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right square bracket while'
-  . ' parsing array at line 1, offset 3', 'right error';
-is $json->decode('{{}'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string while'
-  . ' parsing object at line 1, offset 1', 'right error';
-is $json->decode("[\"foo\x00]"), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected character or invalid escape while'
-  . ' parsing string at line 1, offset 5', 'right error';
-is $json->decode('{"foo":"bar"{'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right curly bracket while'
-  . ' parsing object at line 1, offset 12', 'right error';
-is $json->decode('{"foo""bar"}'), undef, 'syntax error';
-is $json->error,
-  'Malformed JSON: Expected colon while parsing object at line 1, offset 6',
+like $@, qr/bracket while parsing object at line 1, offset 12/, 'right error';
+eval { decode_json '{"foo""bar"}' };
+like $@,
+  qr/Malformed JSON: Expected colon while parsing object at line 1, offset 6/,
   'right error';
-is $json->decode('[[]...'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right square bracket while'
-  . ' parsing array at line 1, offset 3', 'right error';
-is $json->decode('{{}...'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string while'
-  . ' parsing object at line 1, offset 1', 'right error';
-is $json->decode('[nan]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string, array, object, number,'
-  . ' boolean or null at line 1, offset 1', 'right error';
-is $json->decode('["foo]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unterminated string at line 1, offset 6',
+eval { decode_json '[[]...' };
+like $@, qr/Malformed JSON: Expected comma or right square bracket/,
   'right error';
-is $json->decode('{"foo":"bar"}lala'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 1, offset 13',
+like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error';
+eval { decode_json '{{}...' };
+like $@,
+  qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/,
   'right error';
-is $json->decode(''), undef, 'missing input';
-is $json->error, 'Missing or empty input', 'right error';
-is $json->decode("[\"foo\",\n\"bar\"]lala"), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 2, offset 6',
+eval { decode_json '[nan]' };
+like $@, qr/Malformed JSON: Expected string, array, object, number/,
   'right error';
-is $json->decode("[\"foo\",\n\"bar\",\n\"bazra\"]lalala"), undef,
-  'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 3, offset 8',
+like $@, qr/number, boolean or null at line 1, offset 1/, 'right error';
+eval { decode_json '["foo]' };
+like $@, qr/Malformed JSON: Unterminated string at line 1, offset 6/,
   'right error';
-is $json->decode(encode('Shift_JIS', 'やった')), undef, 'invalid encoding';
-is $json->error, 'Input is not UTF-8 encoded', 'right error';
+eval { decode_json '{"foo":"bar"}lala' };
+like $@, qr/Malformed JSON: Unexpected data at line 1, offset 13/,
+  'right error';
+eval { decode_json '' };
+like $@, qr/Missing or empty input/, 'right error';
+eval { decode_json "[\"foo\",\n\"bar\"]lala" };
+like $@, qr/Malformed JSON: Unexpected data at line 2, offset 6/,
+  'right error';
+eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
+like $@, qr/Malformed JSON: Unexpected data at line 3, offset 8/,
+  'right error';
+eval { decode_json '["♥"]' };
+like $@, qr/Input is not UTF-8 encoded/, 'right error';
+eval { decode_json encode('Shift_JIS', 'やった') };
+like $@, qr/Input is not UTF-8 encoded/, 'right error';
 is j('{'), undef, 'syntax error';
-eval { decode_json("[\"foo\",\n\"bar\",\n\"bazra\"]lalala") };
+eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
+like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/,
+  'right error';
+eval { from_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
 like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/,
   'right error';
 
@@ -4,11 +4,11 @@ use Test::More;
 use Mojo::JSON::Pointer;
 
 # "contains" (hash)
-my $pointer = Mojo::JSON::Pointer->new;
-ok $pointer->contains({foo => 23}, ''),     'contains ""';
-ok $pointer->contains({foo => 23}, '/foo'), 'contains "/foo"';
-ok !$pointer->contains({foo => 23}, '/bar'), 'does not contains "/bar"';
-ok $pointer->contains({foo => {bar => undef}}, '/foo/bar'),
+my $pointer = Mojo::JSON::Pointer->new({foo => 23});
+ok $pointer->contains(''),     'contains ""';
+ok $pointer->contains('/foo'), 'contains "/foo"';
+ok !$pointer->contains('/bar'), 'does not contains "/bar"';
+ok $pointer->new({foo => {bar => undef}})->contains('/foo/bar'),
   'contains "/foo/bar"';
 
 # "contains" (mixed)
@@ -20,16 +20,17 @@ ok !$pointer->contains('/foo/bar'), 'does not contain "/foo/bar"';
 ok !$pointer->contains('/0'),       'does not contain "/0"';
 
 # "get" (hash)
-is_deeply $pointer->get({foo => 'bar'}, ''), {foo => 'bar'},
-  '"" is "{foo => "bar"}"';
-is $pointer->get({foo => 'bar'}, '/foo'), 'bar', '"/foo" is "bar"';
-is $pointer->get({foo => {bar => 42}}, '/foo/bar'), 42, '"/foo/bar" is "42"';
-is_deeply $pointer->get({foo => {23 => {baz => 0}}}, '/foo/23'), {baz => 0},
-  '"/foo/23" is "{baz => 0}"';
+$pointer = Mojo::JSON::Pointer->new({foo => 'bar'});
+is_deeply $pointer->get(''), {foo => 'bar'}, '"" is "{foo => "bar"}"';
+is $pointer->get('/foo'), 'bar', '"/foo" is "bar"';
+is $pointer->new({foo => {bar => 42}})->get('/foo/bar'), 42,
+  '"/foo/bar" is "42"';
+is_deeply $pointer->new({foo => {23 => {baz => 0}}})->get('/foo/23'),
+  {baz => 0}, '"/foo/23" is "{baz => 0}"';
 
 # "get" (mixed)
-is_deeply $pointer->get({foo => {bar => [1, 2, 3]}}, '/foo/bar'), [1, 2, 3],
-  '"/foo/bar" is "[1, 2, 3]"';
+is_deeply $pointer->new({foo => {bar => [1, 2, 3]}})->get('/foo/bar'),
+  [1, 2, 3], '"/foo/bar" is "[1, 2, 3]"';
 $pointer = Mojo::JSON::Pointer->new({foo => {bar => [0, undef, 3]}});
 is $pointer->get('/foo/bar/0'), 0,     '"/foo/bar/0" is "0"';
 is $pointer->get('/foo/bar/1'), undef, '"/foo/bar/1" is "undef"';
@@ -37,19 +38,20 @@ is $pointer->get('/foo/bar/2'), 3,     '"/foo/bar/2" is "3"';
 is $pointer->get('/foo/bar/6'), undef, '"/foo/bar/6" is "undef"';
 
 # "get" (encoded)
-is $pointer->get([{'foo/bar' => 'bar'}], '/0/foo~1bar'), 'bar',
+is $pointer->new([{'foo/bar' => 'bar'}])->get('/0/foo~1bar'), 'bar',
   '"/0/foo~1bar" is "bar"';
-is $pointer->get([{'foo/bar/baz' => 'yada'}], '/0/foo~1bar~1baz'), 'yada',
-  '"/0/foo~1bar~1baz" is "yada"';
-is $pointer->get([{'foo~/bar' => 'bar'}], '/0/foo~0~1bar'), 'bar',
+is $pointer->new([{'foo/bar/baz' => 'yada'}])->get('/0/foo~1bar~1baz'),
+  'yada', '"/0/foo~1bar~1baz" is "yada"';
+is $pointer->new([{'foo~/bar' => 'bar'}])->get('/0/foo~0~1bar'), 'bar',
   '"/0/foo~0~1bar" is "bar"';
-is $pointer->get(
-  [{'f~o~o~/b~' => {'a~' => {'r' => 'baz'}}}] => '/0/f~0o~0o~0~1b~0/a~0/r'),
-  'baz', '"/0/f~0o~0o~0~1b~0/a~0/r" is "baz"';
+is $pointer->new([{'f~o~o~/b~' => {'a~' => {'r' => 'baz'}}}])
+  ->get('/0/f~0o~0o~0~1b~0/a~0/r'), 'baz',
+  '"/0/f~0o~0o~0~1b~0/a~0/r" is "baz"';
 
 # Unicode
-is $pointer->get({'☃' => 'snowman'}, '/☃'), 'snowman', 'found the snowman';
-is $pointer->get({'☃' => ['snowman']}, '/☃/0'), 'snowman',
+is $pointer->new({'☃' => 'snowman'})->get('/☃'), 'snowman',
+  'found the snowman';
+is $pointer->new->data({'☃' => ['snowman']})->get('/☃/0'), 'snowman',
   'found the snowman';
 
 # RFC 6901
@@ -0,0 +1,5 @@
+use Mojolicious::Lite;
+
+app->config(script => $0);
+
+app->start;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -33,13 +33,14 @@ is $params2->to_string, 'x=1&y=2', 'right format';
 
 # Param
 is_deeply $params->param('foo'), 'b;ar', 'right structure';
-is_deeply [$params->param('a')], [4, 5], 'right structure';
-is_deeply [$params->param(['a'])], [4], 'right structure';
-is_deeply [$params->param([qw(a foo)])], [4, 'b;ar'], 'right structure';
+is_deeply $params->every_param('foo'), ['b;ar'], 'right structure';
+is_deeply $params->every_param('a'), [4, 5], 'right structure';
+is_deeply [$params->param(['a'])], [5], 'right structure';
+is_deeply [$params->param([qw(a foo)])], [5, 'b;ar'], 'right structure';
 $params->param(foo => 'bar');
 is_deeply [$params->param('foo')], ['bar'], 'right structure';
-is_deeply [$params->param(foo => qw(baz yada))->param('foo')], [qw(baz yada)],
-  'right structure';
+is_deeply $params->param(foo => qw(baz yada))->every_param('foo'),
+  [qw(baz yada)], 'right structure';
 
 # Remove
 $params->parse('q=1&w=2&e=3&e=4&r=6&t=7');
@@ -65,9 +66,12 @@ is $params->to_string, 'bar=bar&foo=', 'right format';
 # "0"
 $params = Mojo::Parameters->new(foo => 0);
 is $params->param('foo'), 0, 'right value';
+is_deeply $params->every_param('foo'), [0], 'right value';
+is_deeply $params->every_param('bar'), [], 'no values';
 is $params->to_string, 'foo=0', 'right format';
 $params = Mojo::Parameters->new($params->to_string);
 is $params->param('foo'), 0, 'right value';
+is_deeply $params->every_param('foo'), [0], 'right value';
 is $params->to_hash->{foo}, 0, 'right value';
 is_deeply $params->to_hash, {foo => 0}, 'right structure';
 is $params->to_string, 'foo=0', 'right format';
@@ -123,9 +127,9 @@ is "$params", 'a=works+too', 'right format';
 # Array values
 $params = Mojo::Parameters->new;
 $params->append(foo => [qw(bar baz)], bar => [qw(bas test)], a => 'b');
-is_deeply [$params->param('foo')], [qw(bar baz)], 'right values';
+is_deeply $params->every_param('foo'), [qw(bar baz)], 'right values';
 is $params->param('a'), 'b', 'right value';
-is_deeply [$params->param('bar')], [qw(bas test)], 'right values';
+is_deeply $params->every_param('bar'), [qw(bas test)], 'right values';
 is_deeply $params->to_hash,
   {foo => ['bar', 'baz'], a => 'b', bar => ['bas', 'test']}, 'right structure';
 $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az']);
@@ -133,14 +137,14 @@ is_deeply $params->to_hash, {foo => ['ba;r', 'b;az']}, 'right structure';
 $params->append(foo => ['bar'], foo => ['baz', 'yada']);
 is_deeply $params->to_hash, {foo => ['ba;r', 'b;az', 'bar', 'baz', 'yada']},
   'right structure';
-is $params->param('foo'), 'ba;r', 'right value';
-is_deeply [$params->param('foo')], [qw(ba;r b;az bar baz yada)],
+is $params->param('foo'), 'yada', 'right value';
+is_deeply $params->every_param('foo'), [qw(ba;r b;az bar baz yada)],
   'right values';
 $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az'], bar => 23);
 is_deeply $params->to_hash, {foo => ['ba;r', 'b;az'], bar => 23},
   'right structure';
-is $params->param('foo'), 'ba;r', 'right value';
-is_deeply [$params->param('foo')], [qw(ba;r b;az)], 'right values';
+is $params->param('foo'), 'b;az', 'right value';
+is_deeply $params->every_param('foo'), [qw(ba;r b;az)], 'right values';
 $params = Mojo::Parameters->new;
 is $params->param(foo => ['ba;r', 'baz'])->to_string, 'foo=ba%3Br&foo=baz',
   'right format';
@@ -19,6 +19,7 @@ ok $path->trailing_slash, 'has trailing slash';
 $path = Mojo::Path->new;
 is $path->to_string,     '',  'no path';
 is $path->to_abs_string, '/', 'right absolute path';
+is $path->to_route,      '/', 'right route';
 
 # Advanced
 $path = Mojo::Path->new('/AZaz09-._~!$&\'()*+,;=:@');
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -50,7 +47,7 @@ $prefork->once(
   heartbeat => sub {
     my ($prefork, $pid) = @_;
     $worker = $pid;
-    $tx     = Mojo::UserAgent->new->get("http://localhost:$port");
+    $tx     = Mojo::UserAgent->new->get("http://127.0.0.1:$port");
     kill 'QUIT', $$;
   }
 );
@@ -103,7 +100,7 @@ my $count = $tx = $graceful, undef;
 $prefork->on(spawn => sub { push @spawn, pop });
 $prefork->once(
   heartbeat => sub {
-    $tx = Mojo::UserAgent->new->get("http://localhost:$port");
+    $tx = Mojo::UserAgent->new->get("http://127.0.0.1:$port");
     kill 'TERM', $$;
   }
 );
@@ -1,7 +1,5 @@
 use Mojo::Base -strict;
 
-BEGIN { $ENV{MOJO_NO_IPV6} = 1 }
-
 use Test::More;
 
 plan skip_all => 'set TEST_EV to enable this test (developer only!)'
@@ -240,37 +238,38 @@ is(Mojo::Reactor->detect, 'Mojo::Reactor::Test', 'right class');
 $ENV{MOJO_REACTOR} = 'Mojo::Reactor::EV';
 is ref Mojo::IOLoop->singleton->reactor, 'Mojo::Reactor::EV', 'right object';
 ok !Mojo::IOLoop->is_running, 'loop is not running';
-my ($server_err, $server_running, $client_err, $client_running);
-$server = $client = '';
+my ($buffer, $server_err, $server_running, $client_err, $client_running);
 $id = Mojo::IOLoop->server(
   {address => '127.0.0.1'} => sub {
     my ($loop, $stream) = @_;
     $stream->write('test' => sub { shift->write('321') });
-    $stream->on(read => sub { $server .= pop });
     $server_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $server_err = $@;
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 Mojo::IOLoop->client(
   {port => $port} => sub {
     my ($loop, $err, $stream) = @_;
-    $stream->write('tset' => sub { shift->write('123') });
-    $stream->on(read => sub { $client .= pop });
+    $stream->on(
+      read => sub {
+        my ($stream, $chunk) = @_;
+        $buffer .= $chunk;
+        return unless $buffer eq 'test321';
+        EV::break(EV::BREAK_ALL());
+      }
+    );
     $client_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $client_err = $@;
   }
 );
-Mojo::IOLoop->timer(1 => sub { EV::break(EV::BREAK_ALL()) });
 EV::run();
 ok !Mojo::IOLoop->is_running, 'loop is not running';
 like $server_err, qr/^Mojo::IOLoop already running/, 'right error';
 like $client_err, qr/^Mojo::IOLoop already running/, 'right error';
 ok $server_running, 'loop is running';
 ok $client_running, 'loop is running';
-is $server,         'tset123', 'right content';
-is $client,         'test321', 'right content';
 
 done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use IO::Socket::INET;
@@ -238,37 +235,38 @@ is(Mojo::Reactor->detect, 'Mojo::Reactor::Test', 'right class');
 $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 is ref Mojo::IOLoop->singleton->reactor, 'Mojo::Reactor::Poll', 'right object';
 ok !Mojo::IOLoop->is_running, 'loop is not running';
-my ($server_err, $server_running, $client_err, $client_running);
-$server = $client = '';
+my ($buffer, $server_err, $server_running, $client_err, $client_running);
 $id = Mojo::IOLoop->server(
   {address => '127.0.0.1'} => sub {
     my ($loop, $stream) = @_;
     $stream->write('test' => sub { shift->write('321') });
-    $stream->on(read => sub { $server .= pop });
     $server_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $server_err = $@;
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
+$port = Mojo::IOLoop->acceptor($id)->port;
 Mojo::IOLoop->client(
   {port => $port} => sub {
     my ($loop, $err, $stream) = @_;
-    $stream->write('tset' => sub { shift->write('123') });
-    $stream->on(read => sub { $client .= pop });
+    $stream->on(
+      read => sub {
+        my ($stream, $chunk) = @_;
+        $buffer .= $chunk;
+        return unless $buffer eq 'test321';
+        Mojo::IOLoop->singleton->reactor->stop;
+      }
+    );
     $client_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $client_err = $@;
   }
 );
-Mojo::IOLoop->timer(1 => sub { Mojo::IOLoop->singleton->reactor->stop });
 Mojo::IOLoop->singleton->reactor->start;
 ok !Mojo::IOLoop->is_running, 'loop is not running';
 like $server_err, qr/^Mojo::IOLoop already running/, 'right error';
 like $client_err, qr/^Mojo::IOLoop already running/, 'right error';
 ok $server_running, 'loop is running';
 ok $client_running, 'loop is running';
-is $server,         'tset123', 'right content';
-is $client,         'test321', 'right content';
 
 done_testing();
@@ -13,16 +13,21 @@ use Mojo::Util 'encode';
 # Parse HTTP 1.1 message with huge "Cookie" header exceeding all limits
 my $req = Mojo::Message::Request->new;
 my $finished;
+$req->max_message_size($req->headers->max_line_size);
+my $huge = 'a=b; ' x $req->max_message_size;
 $req->on(finish => sub { $finished = shift->is_finished });
-$req->parse("GET / HTTP/1.1\x0d\x0a");
-$req->parse('Cookie: ' . ('a=b; ' x (1024 * 1024 * 2)) . "\x0d\x0a");
+ok !$req->is_limit_exceeded, 'limit is not exceeded';
+$req->parse("PUT /upload HTTP/1.1\x0d\x0aCookie: $huge\x0d\x0a");
+ok $req->is_limit_exceeded, 'limit is exceeded';
 $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
 ok $finished, 'finish event has been emitted';
 ok $req->is_finished, 'request is finished';
+is $req->content->leftovers, '', 'no leftovers';
 is $req->error->{message}, 'Maximum message size exceeded', 'right error';
-is $req->method,  'GET', 'right method';
-is $req->version, '1.1', 'right version';
-is $req->url,     '/',   'right URL';
+ok $req->is_limit_exceeded, 'limit is exceeded';
+is $req->method,            'PUT', 'right method';
+is $req->version,           '1.1', 'right version';
+is $req->url,               '/upload', 'right URL';
 is $req->cookie('a'), undef, 'no value';
 
 # Parse HTTP 1.1 message with huge "Cookie" header exceeding line limit
@@ -33,9 +38,10 @@ $req->parse('Cookie: ' . ('a=b; ' x 131072) . "\x0d\x0a");
 $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
 ok $req->is_finished, 'request is finished';
 is $req->error->{message}, 'Maximum line size exceeded', 'right error';
-is $req->method,  'GET', 'right method';
-is $req->version, '1.1', 'right version';
-is $req->url,     '/',   'right URL';
+ok $req->is_limit_exceeded, 'limit is exceeded';
+is $req->method,            'GET', 'right method';
+is $req->version,           '1.1', 'right version';
+is $req->url,               '/', 'right URL';
 is $req->cookie('a'), undef, 'no value';
 is $req->body, '', 'no content';
 
@@ -48,9 +54,10 @@ $req->parse("Content-Length: 4\x0d\x0aCookie: "
     . "\x0d\x0aX-Test: 23\x0d\x0a\x0d\x0aabcd");
 ok $req->is_finished, 'request is finished';
 is $req->error->{message}, 'Maximum line size exceeded', 'right error';
-is $req->method,  'GET', 'right method';
-is $req->version, '1.1', 'right version';
-is $req->url,     '/',   'right URL';
+ok $req->is_limit_exceeded, 'limit is exceeded';
+is $req->method,            'GET', 'right method';
+is $req->version,           '1.1', 'right version';
+is $req->url,               '/', 'right URL';
 is $req->cookie('a'), undef, 'no value';
 is $req->body, '', 'no content';
 
@@ -72,6 +79,7 @@ $req->parse("12345\x0d\x0a");
 ok $req->is_finished, 'request is finished';
 is $req->error->{message}, 'Bad request start line', 'right error';
 is $req->error->{advice}, 400, 'right advice';
+ok !$req->is_limit_exceeded, 'limit is not exceeded';
 
 # Parse broken HTTP 1.1 message with header exceeding line limit
 $req = Mojo::Message::Request->new;
@@ -587,12 +595,12 @@ is_deeply $req->body_params->to_hash->{foo}, [qw(bar bar)], 'right values';
 is $req->body_params->to_hash->{' tset'}, '23 ', 'right value';
 is $req->body_params, 'foo=bar&+tset=23+&foo=bar', 'right parameters';
 is_deeply $req->params->to_hash->{foo}, [qw(bar bar 13)], 'right values';
-is_deeply [$req->param('foo')], [qw(bar bar 13)], 'right values';
+is_deeply $req->every_param('foo'),     [qw(bar bar 13)], 'right values';
 is $req->param(' tset'), '23 ', 'right value';
 $req->param('set', 'single');
 is $req->param('set'), 'single', 'setting single param works';
 $req->param('multi', 1, 2, 3);
-is_deeply [$req->param('multi')], [qw(1 2 3)],
+is_deeply $req->every_param('multi'), [qw(1 2 3)],
   'setting multiple value param works';
 is $req->param('test23'), undef, 'no value';
 
@@ -1433,7 +1441,7 @@ is $req->version,     '1.1', 'right version';
 is $req->url,         '/foo/bar', 'right URL';
 is $req->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
 is $req->headers->host, '127.0.0.1', 'right "Host" value';
-is $req->headers->content_length, '104', 'right "Content-Length" value';
+is $req->headers->content_length, '106', 'right "Content-Length" value';
 is $req->headers->content_type, 'multipart/mixed; boundary=7am1X',
   'right "Content-Type" value';
 is $req->content->parts->[0]->asset->slurp, 'Hallo Welt lalalala!',
@@ -1463,7 +1471,7 @@ is $req->version,     '1.1', 'right version';
 is $req->url,         '/foo/bar', 'right URL';
 is $req->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
 is $req->headers->host, '127.0.0.1', 'right "Host" value';
-is $req->headers->content_length, '104', 'right "Content-Length" value';
+is $req->headers->content_length, '106', 'right "Content-Length" value';
 is $req->headers->content_type, 'multipart/mixed; boundary=7am1X',
   'right "Content-Type" value';
 is $req->content->parts->[0]->asset->slurp, 'Hallo Welt lalalala!',
@@ -1479,7 +1487,7 @@ is $clone->version,     '1.1', 'right version';
 is $clone->url,         '/foo/bar', 'right URL';
 is $clone->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
 is $clone->headers->host, '127.0.0.1', 'right "Host" value';
-is $clone->headers->content_length, '104', 'right "Content-Length" value';
+is $clone->headers->content_length, '106', 'right "Content-Length" value';
 is $clone->headers->content_type, 'multipart/mixed; boundary=7am1X',
   'right "Content-Type" value';
 is $clone->content->parts->[0]->asset->slurp, 'Hallo Welt lalalala!',
@@ -1587,10 +1595,11 @@ is $req2->headers->cookie, 'foo=bar; foo=baz; foo=yada; bar=foo',
   'right "Cookie" value';
 is $req2->url, '/foo/bar', 'right URL';
 is $req2->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
-is_deeply [map { $_->value } $req2->cookie('foo')], [qw(bar baz yada)],
+is_deeply [map { $_->value } @{$req2->every_cookie('foo')}],
+  [qw(bar baz yada)], 'right values';
+is_deeply [map { $_->value } @{$req2->every_cookie('bar')}], ['foo'],
   'right values';
-is_deeply [map { $_->value } $req2->cookie('bar')], ['foo'], 'right values';
-is_deeply [map { $_->value } $req2->cookie([qw(foo bar)])], [qw(bar foo)],
+is_deeply [map { $_->value } $req2->cookie([qw(foo bar)])], [qw(yada foo)],
   'right values';
 
 # Parse full HTTP 1.0 request with cookies and progress callback
@@ -1644,6 +1653,46 @@ is $cookies->[0]->value, 'bar', 'right value';
 is $cookies->[1]->name,  'bar', 'right name';
 is $cookies->[1]->value, 'baz', 'right value';
 
+# Parse and clone multipart/form-data request (changing size)
+$req = Mojo::Message::Request->new;
+$req->parse("POST /example/testform_handler HTTP/1.1\x0d\x0a");
+$req->parse("User-Agent: Mozilla/5.0\x0d\x0a");
+$req->parse('Content-Type: multipart/form-data; ');
+$req->parse("boundary=----WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
+$req->parse("Content-Length: 318\x0d\x0aConnection: keep-alive\x0d\x0a");
+$req->parse("Host: 127.0.0.1:3000\x0d\x0a\x0d\x0a");
+$req->parse("------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
+$req->parse("Content-Disposition: form-data; name=\"Vorname\"\x0a");
+$req->parse("\x0d\x0aT\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
+$req->parse("\x0aContent-Disposition: form-data; name=\"Zuname\"\x0a");
+$req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
+$req->parse("\x0aContent-Disposition: form-data; name=\"Text\"\x0a");
+$req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP--");
+ok $req->is_finished, 'request is finished';
+is $req->method,      'POST', 'right method';
+is $req->version,     '1.1', 'right version';
+is $req->url,         '/example/testform_handler', 'right URL';
+is $req->headers->content_length, 318, 'right "Content-Length" value';
+is $req->param('Vorname'), 'T', 'right value';
+is $req->param('Zuname'),  '',  'right value';
+is $req->param('Text'),    '',  'right value';
+is $req->content->parts->[0]->asset->slurp, 'T', 'right content';
+is $req->content->leftovers, '', 'no leftovers';
+$req = Mojo::Message::Request->new->parse($req->clone->to_string);
+ok $req->is_finished, 'request is finished';
+is $req->method,      'POST', 'right method';
+is $req->version,     '1.1', 'right version';
+is $req->url,         '/example/testform_handler', 'right URL';
+is $req->headers->content_length, 323, 'right "Content-Length" value';
+is $req->param('Vorname'), 'T', 'right value';
+is $req->param('Zuname'),  '',  'right value';
+is $req->param('Text'),    '',  'right value';
+is $req->content->parts->[0]->asset->slurp, 'T', 'right content';
+is $req->content->leftovers, '', 'no leftovers';
+is $req->content->get_body_chunk(322), "\x0a",      'right chunk';
+is $req->content->get_body_chunk(321), "\x0d\x0a",  'right chunk';
+is $req->content->get_body_chunk(320), "-\x0d\x0a", 'right chunk';
+
 # Parse multipart/form-data request with charset
 $req = Mojo::Message::Request->new;
 is $req->default_charset, 'UTF-8', 'default charset is UTF-8';
@@ -1674,7 +1723,7 @@ $req->parse("POST /example/testform_handler HTTP/1.1\x0d\x0a");
 $req->parse("User-Agent: Mozilla/5.0\x0d\x0a");
 $req->parse('Content-Type: multipart/form-data; ');
 $req->parse("boundary=----WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
-$req->parse("Content-Length: 321\x0d\x0aConnection: keep-alive\x0d\x0a");
+$req->parse("Content-Length: 323\x0d\x0aConnection: keep-alive\x0d\x0a");
 $req->parse("Host: 127.0.0.1:3000\x0d\x0a\x0d\x0a");
 $req->parse("------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
 $req->parse("Content-Disposition: form-data; name=\"Vorname\"\x0d\x0a");
@@ -1685,6 +1734,8 @@ $req->parse("\x0aContent-Disposition: form-data; name=\"Text\"\x0d\x0a");
 $req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP-");
 ok !$req->is_finished, 'request is not finished';
 $req->parse('-');
+ok !$req->is_finished, 'request is not finished';
+$req->parse("\x0d\x0a");
 ok $req->is_finished, 'request is finished';
 is $req->method,      'POST', 'right method';
 is $req->version,     '1.1', 'right version';
@@ -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';
@@ -82,6 +80,7 @@ is $res->code(509)->default_message, 'Bandwidth Limit Exceeded',
 is $res->code(510)->default_message, 'Not Extended', 'right message';
 is $res->code(511)->default_message, 'Network Authentication Required',
   'right message';
+is $res->default_message(100), 'Continue', 'right message';
 
 # Status code ranges
 ok $res->code(200)->is_status_class(200), 'is in range';
@@ -272,6 +271,7 @@ $res->parse("Hello World!\n1234\nlalalala\n");
 ok !$res->is_finished, 'response is not finished';
 ok !$res->is_empty,    'response is not empty';
 ok !$res->content->skip_body, 'body has not been skipped';
+ok $res->content->relaxed, 'relaxed response';
 is $res->code,    500,                     'right status';
 is $res->message, 'Internal Server Error', 'right message';
 is $res->version, '1.1',                   'right version';
@@ -279,6 +279,19 @@ is $res->headers->content_type,   'text/plain', 'right "Content-Type" value';
 is $res->headers->content_length, undef,        'no "Content-Length" value';
 is $res->body, "Hello World!\n1234\nlalalala\n", 'right content';
 
+# Parse full HTTP 1.1 response (broken Content-Length)
+$res = Mojo::Message::Response->new;
+$res->parse("HTTP/1.1 200 OK\x0d\x0a");
+$res->parse("Content-Length: 123test\x0d\x0a\x0d\x0a");
+$res->parse('Hello World!');
+ok $res->is_finished, 'response is finished';
+is $res->code,        200, 'right status';
+is $res->message,     'OK', 'right message';
+is $res->version,     '1.1', 'right version';
+is $res->headers->content_length, '123test', 'right "Content-Length" value';
+is $res->body, '', 'no content';
+is $res->content->leftovers, 'Hello World!', 'content in leftovers';
+
 # Parse full HTTP 1.1 response (100 Continue)
 $res = Mojo::Message::Response->new;
 $res->content->on(body => sub { shift->headers->header('X-Body' => 'one') });
@@ -355,14 +368,16 @@ is $res->headers->content_length, undef, 'right "Content-Length" value';
   $res->parse("HTTP/1.1 200 OK\x0d\x0a");
   $res->parse("Content-Type: text/plain\x0d\x0a");
   $res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
+  ok !$res->is_limit_exceeded, 'limit is not exceeded';
   $res->parse('a' x 1000);
   ok $res->is_finished, 'response is finished';
   ok $res->content->is_finished, 'content is finished';
   is $res->error->{message}, 'Maximum buffer size exceeded', 'right error';
   is $res->error->{advice}, 400, 'right advice';
-  is $res->code,    200,   'right status';
-  is $res->message, 'OK',  'right message';
-  is $res->version, '1.1', 'right version';
+  ok $res->is_limit_exceeded, 'limit is not exceeded';
+  is $res->code,              200, 'right status';
+  is $res->message,           'OK', 'right message';
+  is $res->version,           '1.1', 'right version';
   is $res->headers->content_type, 'text/plain', 'right "Content-Type" value';
 }
 
@@ -464,7 +479,7 @@ isa_ok $res->content->parts->[2], 'Mojo::Content::Single', 'right part';
 is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n",
   'right content';
 
-# Parse HTTP 1.1 chunked multipart response (at once)
+# Parse HTTP 1.1 chunked multipart response with leftovers (at once)
 $res = Mojo::Message::Response->new;
 my $multipart
   = "HTTP/1.1 200 OK\x0d\x0a"
@@ -486,7 +501,8 @@ my $multipart
   . "print \"Hello World :)\\n\"\n"
   . "\x0d\x0a------------0xKhTmLbOuNdA"
   . "r\x0d\x0a3\x0d\x0aY--\x0d\x0a"
-  . "0\x0d\x0a\x0d\x0a";
+  . "0\x0d\x0a\x0d\x0a"
+  . "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a";
 $res->parse($multipart);
 ok $res->is_finished, 'response is finished';
 is $res->code,        200, 'right status';
@@ -508,6 +524,8 @@ isa_ok $res->upload('upload')->asset, 'Mojo::Asset::Memory', 'right file';
 is $res->upload('upload')->asset->size, 69, 'right size';
 is $res->content->parts->[2]->headers->content_type,
   'application/octet-stream', 'right "Content-Type" value';
+is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a",
+  'next response in leftovers';
 
 # Parse HTTP 1.1 chunked multipart response (in multiple small chunks)
 $res = Mojo::Message::Response->new;
@@ -755,7 +773,7 @@ is $res->code,        200, 'right status';
 is $res->message,     'OK', 'right message';
 is $res->version,     '1.1', 'right version';
 is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value';
-is $res->headers->content_length, '108', 'right "Content-Length" value';
+is $res->headers->content_length, '110', 'right "Content-Length" value';
 is $res->headers->content_type, 'multipart/mixed; boundary=7am1X',
   'right "Content-Type" value';
 is $res->content->parts->[0]->asset->slurp, 'Hallo Welt lalalalalala!',
@@ -1022,18 +1040,18 @@ is $res->version,     '1.1', 'right version';
 is $res->dom->at('p')->text,     'foo', 'right value';
 is $res->dom->at('p > a')->text, 'bar', 'right value';
 is $res->dom('p')->first->text, 'foo', 'right value';
-is_deeply [$res->dom('p > a')->pluck('text')->each], [qw(bar baz)],
+is_deeply $res->dom('p > a')->map('text')->to_array, [qw(bar baz)],
   'right values';
-my @text = $res->dom('a')->pluck(content => 'yada')->first->root->find('p > a')
-  ->pluck('text')->each;
+my @text = $res->dom('a')->map(content => 'yada')->first->root->find('p > a')
+  ->map('text')->each;
 is_deeply \@text, [qw(yada yada)], 'right values';
-is_deeply [$res->dom('p > a')->pluck('text')->each], [qw(yada yada)],
+is_deeply $res->dom('p > a')->map('text')->to_array, [qw(yada yada)],
   'right values';
 @text
-  = $res->dom->find('a')->pluck(content => 'test')->first->root->find('p > a')
-  ->pluck('text')->each;
+  = $res->dom->find('a')->map(content => 'test')->first->root->find('p > a')
+  ->map('text')->each;
 is_deeply \@text, [qw(test test)], 'right values';
-is_deeply [$res->dom->find('p > a')->pluck('text')->each], [qw(test test)],
+is_deeply $res->dom->find('p > a')->map('text')->to_array, [qw(test test)],
   'right values';
 
 # Build DOM from response with charset
@@ -16,8 +16,9 @@ package main;
 use Mojo::Base -strict;
 
 use Test::More;
-use File::Spec::Functions qw(catfile splitdir);
-use FindBin;
+use Cwd 'abs_path';
+use File::Basename 'dirname';
+use File::Spec::Functions 'catfile';
 use Mojo::Template;
 
 # Capture helper
@@ -79,6 +80,11 @@ $mt     = Mojo::Template->new;
 $output = $mt->render('    <%= "one" =%><%= "two" %>  three');
 is $output, "onetwo  three\n", 'expression tags trimmed';
 
+# Nothing to trim
+$mt     = Mojo::Template->new;
+$output = $mt->render('<% =%>');
+is $output, '', 'nothing trimmed';
+
 # Replace tag
 $mt     = Mojo::Template->new;
 $output = $mt->render('<%% 1 + 1 %>');
@@ -652,7 +658,8 @@ like "$output", qr/ohoh/, 'right result';
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 test
-123
+123\
+456
  %# This dies
 % die 'oops!';
 %= 1 + 1
@@ -663,21 +670,23 @@ like $output->message, qr/oops!/, 'right message';
 is $output->lines_before->[0][0], 1,               'right number';
 is $output->lines_before->[0][1], 'test',          'right line';
 is $output->lines_before->[1][0], 2,               'right number';
-is $output->lines_before->[1][1], '123',           'right line';
+is $output->lines_before->[1][1], '123\\',         'right line';
 is $output->lines_before->[2][0], 3,               'right number';
-is $output->lines_before->[2][1], ' %# This dies', 'right line';
-is $output->line->[0], 4, 'right number';
+is $output->lines_before->[2][1], '456',           'right line';
+is $output->lines_before->[3][0], 4,               'right number';
+is $output->lines_before->[3][1], ' %# This dies', 'right line';
+is $output->line->[0], 5, 'right number';
 is $output->line->[1], "% die 'oops!';", 'right line';
-is $output->lines_after->[0][0], 5,          'right number';
+is $output->lines_after->[0][0], 6,          'right number';
 is $output->lines_after->[0][1], '%= 1 + 1', 'right line';
-is $output->lines_after->[1][0], 6,          'right number';
+is $output->lines_after->[1][0], 7,          'right number';
 is $output->lines_after->[1][1], 'test',     'right line';
-like "$output", qr/oops! at template line 4/, 'right result';
+like "$output", qr/oops! at template line 5/, 'right result';
 
 # Exception in template (empty perl lines)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
-test
+test\\
 123
 %
 % die 'oops!';
@@ -689,15 +698,15 @@ test
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/oops!/, 'right message';
-is $output->lines_before->[0][0], 1,      'right number';
-is $output->lines_before->[0][1], 'test', 'right line';
+is $output->lines_before->[0][0], 1,          'right number';
+is $output->lines_before->[0][1], 'test\\\\', 'right line';
 ok $output->lines_before->[0][2], 'contains code';
-is $output->lines_before->[1][0], 2,      'right number';
-is $output->lines_before->[1][1], '123',  'right line';
+is $output->lines_before->[1][0], 2,          'right number';
+is $output->lines_before->[1][1], '123',      'right line';
 ok $output->lines_before->[1][2], 'contains code';
-is $output->lines_before->[2][0], 3,      'right number';
-is $output->lines_before->[2][1], '%',    'right line';
-is $output->lines_before->[2][2], ' ',    'right code';
+is $output->lines_before->[2][0], 3,          'right number';
+is $output->lines_before->[2][1], '%',        'right line';
+is $output->lines_before->[2][2], ' ',        'right code';
 is $output->line->[0], 4, 'right number';
 is $output->line->[1], "% die 'oops!';", 'right line';
 is $output->lines_after->[0][0], 5,     'right number';
@@ -999,6 +1008,26 @@ $output = $mt->render(<<'EOF');
 EOF
 is $output, "hello world\n", 'escaped multiline expression';
 
+# Empty statement
+$mt     = Mojo::Template->new;
+$output = $mt->render("test\n\n123\n\n<% %>456\n789");
+is $output, "test\n\n123\n\n456\n789\n", 'empty statement';
+
+# Optimize successive text lines ending with newlines
+$mt = Mojo::Template->new;
+$mt->parse(<<'EOF');
+test
+123
+456\
+789\\
+987
+654
+321
+EOF
+is $mt->tree->[0][1], "test\n123\n456", 'optimized text lines';
+$output = $mt->build->compile || $mt->interpret;
+is $output, "test\n123\n456789\\\n987\n654\n321\n", 'just text';
+
 # Scoped scalar
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
@@ -1038,13 +1067,13 @@ EOF
 
 # File
 $mt = Mojo::Template->new;
-my $file = catfile(splitdir($FindBin::Bin), qw(templates test.mt));
+my $file = abs_path catfile(dirname(__FILE__), 'templates', 'test.mt');
 $output = $mt->render_file($file, 3);
 like $output, qr/23\nHello World!/, 'file';
 
 # Exception in file
 $mt     = Mojo::Template->new;
-$file   = catfile(splitdir($FindBin::Bin), qw(templates exception.mt));
+$file   = abs_path catfile(dirname(__FILE__), 'templates', 'exception.mt');
 $output = $mt->render_file($file);
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/exception\.mt line 2/, 'message contains filename';
@@ -1071,8 +1100,8 @@ is $output->lines_after->[0][1], '123', 'right line';
 like "$output", qr/foo\.mt from DATA section line 2/, 'right result';
 
 # Exception with UTF-8 context
-$mt     = Mojo::Template->new;
-$file   = catfile(splitdir($FindBin::Bin), qw(templates utf8_exception.mt));
+$mt = Mojo::Template->new;
+$file = abs_path catfile(dirname(__FILE__), 'templates', 'utf8_exception.mt');
 $output = $mt->render_file($file);
 isa_ok $output, 'Mojo::Exception', 'right exception';
 is $output->lines_before->[0][1], '☃', 'right line';
@@ -1091,7 +1120,7 @@ is $output->lines_after->[0], undef, 'no lines after';
 
 # Different encodings
 $mt = Mojo::Template->new(encoding => 'shift_jis');
-$file = catfile(splitdir($FindBin::Bin), qw(templates utf8_exception.mt));
+$file = abs_path catfile(dirname(__FILE__), 'templates', 'utf8_exception.mt');
 ok !eval { $mt->render_file($file) }, 'file not rendered';
 like $@, qr/invalid encoding/, 'right error';
 
@@ -1,8 +1,6 @@
 use Mojo::Base -strict;
 
 use Test::More;
-use File::Spec::Functions 'catdir';
-use FindBin;
 use Mojo::Asset::File;
 use Mojo::Asset::Memory;
 use Mojo::Transaction::WebSocket;
@@ -134,8 +132,7 @@ is $tx->req->body, 'a=1&a=2&a=3&b=4', 'right content';
 
 # Existing query string (lowercase HEAD)
 $tx = $t->tx(head => 'http://example.com?foo=bar' => form => {baz => [1, 2]});
-is $tx->req->url->to_abs, 'http://example.com?foo=bar&baz=1&baz=2',
-  'right URL';
+is $tx->req->url->to_abs, 'http://example.com?baz=1&baz=2', 'right URL';
 is $tx->req->method, 'head', 'right method';
 is $tx->req->headers->content_type, undef, 'no "Content-Type" value';
 ok $tx->is_empty, 'transaction is empty';
@@ -151,12 +148,14 @@ is $tx->req->headers->content_type, 'application/x-www-form-urlencoded',
   'right "Content-Type" value';
 is $tx->req->body, 'test=12345678912', 'right content';
 
-# UTF-8 form with header
-$tx = $t->tx(POST => 'http://example.com/foo' => {Accept => '*/*'} => form =>
+# UTF-8 form with header and custom content type
+$tx
+  = $t->tx(POST => 'http://example.com/foo' =>
+    {Accept => '*/*', 'Content-Type' => 'application/mojo-form'} => form =>
     {test => 123} => charset => 'UTF-8');
 is $tx->req->url->to_abs, 'http://example.com/foo', 'right URL';
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->headers->content_type, 'application/x-www-form-urlencoded',
+is $tx->req->headers->content_type, 'application/mojo-form',
   'right "Content-Type" value';
 is $tx->req->headers->accept, '*/*', 'right "Accept" value';
 is $tx->req->body, 'test=123', 'right content';
@@ -197,13 +196,12 @@ like $tx->req->content->parts->[3]->headers->content_disposition, qr/"b"/,
   'right "Content-Disposition" value';
 is $tx->req->content->parts->[3]->asset->slurp, 4, 'right part';
 is $tx->req->content->parts->[4], undef, 'no more parts';
-is_deeply [$tx->req->param('a')], [1, 2, 3], 'right values';
+is_deeply $tx->req->every_param('a'), [1, 2, 3], 'right values';
 is_deeply [$tx->req->param('b')], [4], 'right values';
 
 # Multipart form with real file and custom header
-my $path = catdir $FindBin::Bin, 'transactor.t';
 $tx = $t->tx(POST => 'http://example.com/foo' => form =>
-    {mytext => {file => $path, DNT => 1}});
+    {mytext => {file => __FILE__, DNT => 1}});
 is $tx->req->url->to_abs, 'http://example.com/foo', 'right URL';
 is $tx->req->method, 'POST', 'right method';
 is $tx->req->headers->content_type, 'multipart/form-data',
@@ -218,12 +216,14 @@ ok !$tx->req->content->parts->[0]->headers->header('file'), 'no "file" header';
 is $tx->req->content->parts->[0]->headers->dnt, 1, 'right "DNT" header';
 is $tx->req->content->parts->[1], undef, 'no more parts';
 
-# Multipart form with asset
-$tx = $t->tx(POST => 'http://example.com/foo' => form =>
-    {mytext => {file => Mojo::Asset::File->new(path => $path)}});
+# Multipart form with asset and custom content type
+$tx
+  = $t->tx(POST => 'http://example.com/foo' =>
+    {'Content-Type' => 'multipart/mojo-form'} => form =>
+    {mytext => {file => Mojo::Asset::File->new(path => __FILE__)}});
 is $tx->req->url->to_abs, 'http://example.com/foo', 'right URL';
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->headers->content_type, 'multipart/form-data',
+is $tx->req->headers->content_type, 'multipart/mojo-form',
   'right "Content-Type" value';
 like $tx->req->content->parts->[0]->headers->content_disposition,
   qr/"mytext"/, 'right "Content-Disposition" value';
@@ -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');
@@ -422,6 +450,7 @@ ok $tx->req->headers->sec_websocket_version,
 is $tx->req->headers->upgrade, 'websocket', 'right "Upgrade" value';
 is $t->upgrade($tx), undef, 'not upgraded';
 Mojo::Transaction::WebSocket->new(handshake => $tx)->server_handshake;
+$tx->res->code(101);
 $tx = $t->upgrade($tx);
 ok $tx->is_websocket, 'is a WebSocket';
 
@@ -494,6 +523,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(
@@ -758,13 +790,13 @@ is $tx->res->headers->location, undef, 'no "Location" value';
 $tx = $t->tx(
   POST => 'http://mojolicio.us/foo' => {Accept => '*/*'} => 'whatever');
 $tx->res->code(308);
-$tx->res->headers->location('http://example.com/bar');
+$tx->res->headers->location('https://example.com/bar');
 is $tx->req->headers->accept, '*/*', 'right "Accept" value';
 is $tx->req->body, 'whatever', 'right content';
 $tx = $t->redirect($tx);
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->url->to_abs, 'http://example.com/bar', 'right URL';
-is $tx->req->headers->accept, '*/*', 'right "Accept" value';
+is $tx->req->url->to_abs, 'https://example.com/bar', 'right URL';
+is $tx->req->headers->accept,   '*/*', 'right "Accept" value';
 is $tx->req->headers->location, undef, 'no "Location" value';
 is $tx->req->body, 'whatever', 'right content';
 is $tx->res->code, undef,      'no status';
@@ -786,6 +818,12 @@ is $tx->req->headers->accept, 'application/json', 'right "Accept" value';
 is $tx->req->body, '', 'no content';
 is $t->redirect($tx), undef, 'unsupported redirect';
 
+# 302 redirect with bad location
+$tx = $t->tx(GET => 'http://mojolicio.us/foo');
+$tx->res->code(302);
+$tx->res->headers->location('data:image/png;base64,helloworld123');
+is $t->redirect($tx), undef, 'unsupported redirect';
+
 # 302 redirect (relative path and query)
 $tx = $t->tx(
   POST => 'http://mojolicio.us/foo/bar?a=b' => {Accept => 'application/json'});
@@ -1,7 +1,7 @@
 use Mojo::Base -strict;
 
 BEGIN {
-  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_TLS} = 1;
+  $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('/');
@@ -172,14 +179,13 @@ is $tx->res->code, 200, 'right status';
 is $tx->res->headers->connection, 'test', 'right "Connection" value';
 is $tx->res->body, 'One!', 'right content';
 
-# Error in callback is logged
-app->ua->once(error => sub { Mojo::IOLoop->stop });
-ok app->ua->has_subscribers('error'), 'has subscribers';
+# Error in callback
+Mojo::IOLoop->singleton->reactor->unsubscribe('error');
 my $err;
-my $msg = app->log->on(message => sub { $err .= pop });
+Mojo::IOLoop->singleton->reactor->once(
+  error => sub { $err .= pop; Mojo::IOLoop->stop });
 app->ua->get('/' => sub { die 'error event works' });
 Mojo::IOLoop->start;
-app->log->unsubscribe(message => $msg);
 like $err, qr/error event works/, 'right error';
 
 # Events
@@ -282,7 +288,7 @@ is $body,    '{"hello":"world"}', 'right content';
 
 # Built-in web server times out
 my $log = '';
-$msg = app->log->on(message => sub { $log .= pop });
+my $msg = app->log->on(message => sub { $log .= pop });
 $tx = $ua->get('/timeout?timeout=0.25');
 app->log->unsubscribe(message => $msg);
 ok !$tx->success, 'not successful';
@@ -307,9 +313,7 @@ ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Inactivity timeout', 'right error';
 
 # Keep alive connection times out
-my ($fail, $id);
-my $error = $ua->on(error => sub { $fail++ });
-ok $ua->has_subscribers('error'), 'has subscribers';
+my $id;
 $ua->get(
   '/' => sub {
     my ($ua, $tx) = @_;
@@ -319,10 +323,7 @@ $ua->get(
   }
 );
 Mojo::IOLoop->start;
-ok !$fail, 'error event has not been emitted';
 ok !Mojo::IOLoop->stream($id), 'connection timed out';
-$ua->unsubscribe(error => $error);
-ok !$ua->has_subscribers('error'), 'unsubscribed successfully';
 
 # Response exceeding message size limit
 $ua->once(
@@ -344,6 +345,22 @@ ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Not Found', 'right error';
 is $tx->error->{code},    404,         'right status';
 
+# Compressed response
+$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
+$tx = $ua->start($ua->build_tx(GET => '/echo' => 'Hello GZip!'));
+ok $tx->success, 'successful';
+is $tx->res->code, 200, 'right status';
+is $tx->res->headers->content_encoding, undef, 'no "Content-Encoding" value';
+is $tx->res->body, 'Hello GZip!', 'right content';
+$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
+$tx->res->content->auto_decompress(0);
+$tx = $ua->start($tx);
+ok $tx->success, 'successful';
+is $tx->res->code, 200, 'right status';
+is $tx->res->headers->content_encoding, 'gzip',
+  'right "Content-Encoding" value';
+isnt $tx->res->body, 'Hello GZip!', 'different content';
+
 # Fork safety
 $tx = $ua->get('/');
 is $tx->res->body, 'works!', 'right content';
@@ -509,8 +526,8 @@ $id  = Mojo::IOLoop->server(
     );
   }
 );
-$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
-$tx = $ua->build_tx(GET => "http://localhost:$port/");
+$port = Mojo::IOLoop->acceptor($id)->port;
+$tx = $ua->build_tx(GET => "http://127.0.0.1:$port/");
 my @unexpected;
 $tx->on(unexpected => sub { push @unexpected, pop });
 $tx = $ua->start($tx);
@@ -10,8 +10,6 @@ use Mojo::IOLoop::Server;
 
 plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
   unless $ENV{TEST_ONLINE};
-plan skip_all => 'IO::Socket::IP 0.20 required for this test!'
-  unless Mojo::IOLoop::Server::IPV6;
 plan skip_all => 'IO::Socket::SSL 1.84 required for this test!'
   unless Mojo::IOLoop::Server::TLS;
 
@@ -66,7 +64,7 @@ $ua = Mojo::UserAgent->new;
 
 # Connection refused
 my $port = Mojo::IOLoop::Server->generate_port;
-$tx = $ua->build_tx(GET => "http://localhost:$port");
+$tx = $ua->build_tx(GET => "http://127.0.0.1:$port");
 $ua->start($tx);
 ok $tx->is_finished, 'transaction is finished';
 ok $tx->error,       'has error';
@@ -87,7 +85,7 @@ ok $tx->error,       'has error';
 $tx = $ua->build_tx(GET => 'http://cdeabcdeffoobarnonexisting.com');
 $ua->start($tx);
 ok $tx->is_finished, 'transaction is finished';
-like $tx->error->{message}, qr/^Couldn't connect/, 'right error';
+ok $tx->error,       'has error';
 
 # Fresh user agent again
 $ua = Mojo::UserAgent->new;
@@ -234,8 +232,9 @@ is $tx->res->code,   200,             'right status';
 ok $tx->kept_alive,    'connection was kept alive';
 ok $tx->local_address, 'has local address';
 ok $tx->local_port > 0, 'has local port';
-ok $tx->remote_address, 'has local address';
-ok $tx->remote_port > 0, 'has local port';
+ok $tx->original_remote_address, 'has original remote address';
+ok $tx->remote_address,          'has remote address';
+ok $tx->remote_port > 0, 'has remote port';
 
 # Simple request with redirect
 $ua->max_redirects(3);
@@ -0,0 +1,169 @@
+use Mojo::Base -strict;
+
+BEGIN { $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();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -35,21 +32,21 @@ my $listen
   . '&key=t/mojo/certs/server.key'
   . '&ca=t/mojo/certs/ca.crt';
 $daemon->listen([$listen])->start;
-my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->port;
 
 # No certificate
 my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
-my $tx = $ua->get("https://localhost:$port");
+my $tx = $ua->get("https://127.0.0.1:$port");
 ok !$tx->success, 'not successful';
 ok $tx->error, 'has error';
-$tx = $ua->get("https://localhost:$port");
+$tx = $ua->get("https://127.0.0.1:$port");
 ok !$tx->success, 'not successful';
 ok $tx->error, 'has error';
 
 # Valid certificates
 $ua->ca('t/mojo/certs/ca.crt')->cert('t/mojo/certs/client.crt')
   ->key('t/mojo/certs/client.key');
-$tx = $ua->get("https://localhost:$port");
+$tx = $ua->get("https://127.0.0.1:$port");
 ok $tx->success, 'successful';
 is $tx->res->code, 200,      'right status';
 is $tx->res->body, 'works!', 'right content';
@@ -58,7 +55,7 @@ is $tx->res->body, 'works!', 'right content';
 my $sock;
 $ua->ioloop->client(
   {
-    address  => 'localhost',
+    address  => '127.0.0.1',
     port     => $port,
     tls      => 1,
     tls_ca   => 't/mojo/certs/ca.crt',
@@ -86,7 +83,7 @@ $ua = Mojo::UserAgent->new(ioloop => $ua->ioloop);
   local $ENV{MOJO_CA_FILE}   = 't/mojo/certs/ca.crt';
   local $ENV{MOJO_CERT_FILE} = 't/mojo/certs/client.crt';
   local $ENV{MOJO_KEY_FILE}  = 't/mojo/certs/client.key';
-  $tx = $ua->get("https://localhost:$port");
+  $tx = $ua->get("https://127.0.0.1:$port");
   is $ua->ca,   't/mojo/certs/ca.crt',     'right path';
   is $ua->cert, 't/mojo/certs/client.crt', 'right path';
   is $ua->key,  't/mojo/certs/client.key', 'right path';
@@ -97,8 +94,8 @@ $ua = Mojo::UserAgent->new(ioloop => $ua->ioloop);
 
 # Invalid certificate
 $ua = Mojo::UserAgent->new(ioloop => $ua->ioloop);
-$ua->cert('t/mojo/certs/badclient.crt')->key('t/mojo/certs/badclient.key');
-$tx = $ua->get("https://localhost:$port");
+$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
+$tx = $ua->get("https://127.0.0.1:$port");
 ok !$tx->success, 'not successful';
 ok $tx->error, 'has error';
 
@@ -116,12 +113,12 @@ $listen
   . '&ciphers=RC4-SHA:ALL'
   . '&verify=0x00';
 $daemon->listen([$listen])->start;
-$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->port;
 
 # Invalid certificate
 $ua = Mojo::UserAgent->new(ioloop => $ua->ioloop);
-$ua->cert('t/mojo/certs/badclient.crt')->key('t/mojo/certs/badclient.key');
-$tx = $ua->get("https://localhost:$port");
+$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
+$tx = $ua->get("https://127.0.0.1:$port");
 ok $tx->success, 'successful';
 ok !$tx->error, 'no error';
 is $ua->ioloop->stream($tx->connection)->handle->get_cipher, 'RC4-SHA',
@@ -4,8 +4,11 @@ use FindBin;
 use lib "$FindBin::Bin/lib";
 
 use Test::More;
-use File::Spec::Functions qw(catfile splitdir);
+use Cwd 'abs_path';
+use File::Basename 'dirname';
+use File::Spec::Functions 'catfile';
 use File::Temp 'tempdir';
+use Mojo::ByteStream 'b';
 use Mojo::DeprecationTest;
 
 use Mojo::Util
@@ -14,7 +17,7 @@ use Mojo::Util
   qw(monkey_patch punycode_decode punycode_encode quote secure_compare),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
   qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode);
+  qw(xml_escape xor_encode xss_escape);
 
 # camelize
 is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result';
@@ -67,8 +70,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';
@@ -193,6 +196,10 @@ is xml_escape('привет'), 'привет', 'right XML escaped result';
 is xml_escape('привет<foo>'), 'привет&lt;foo&gt;',
   'right XML escaped result';
 
+# xss_escape
+is xss_escape('<p>'), '&lt;p&gt;', 'right XSS escaped result';
+is xss_escape(b('<p>')), '<p>', 'right XSS escaped result';
+
 # punycode_encode
 is punycode_encode('bücher'), 'bcher-kva', 'right punycode encoded result';
 
@@ -371,7 +378,7 @@ is xor_encode('hello', '123456789'), "\x59\x57\x5f\x58\x5a", 'right result';
 is xor_encode("\x59\x57\x5f\x58\x5a", '123456789'), 'hello', 'right result';
 
 # slurp
-is slurp(catfile(splitdir($FindBin::Bin), qw(templates exception.mt))),
+is slurp(abs_path catfile(dirname(__FILE__), 'templates', 'exception.mt')),
   "test\n% die;\n123\n", 'right content';
 
 # spurt
@@ -381,7 +388,7 @@ spurt "just\nworks!", $file;
 is slurp($file), "just\nworks!", 'successful roundtrip';
 
 # steady_time
-like steady_time, qr/^\d+\.\d+$/, 'high resolution time';
+like steady_time, qr/^[\d.]+$/, 'high resolution time';
 
 # monkey_patch
 {
@@ -408,6 +415,15 @@ is MojoMonkeyTest::yin(), 'yin', 'right result';
 ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists';
 is MojoMonkeyTest::yang(), 'yang', 'right result';
 
+# monkey_patch (with name)
+SKIP: {
+  skip 'Sub::Util required!', 2 unless eval { require Sub::Util; 1 };
+  is Sub::Util::subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo',
+    'right name';
+  is Sub::Util::subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar',
+    'right name';
+}
+
 # tablify
 is tablify([["f\r\no o\r\n", 'bar']]),     "fo o  bar\n",      'right result';
 is tablify([["  foo",        '  b a r']]), "  foo    b a r\n", 'right result';
@@ -416,6 +432,8 @@ is tablify([['foo', 'yada'], ['yada', 'yada']]), "foo   yada\nyada  yada\n",
   'right result';
 is tablify([['foo', 'bar', 'baz'], ['yada', 'yada', 'yada']]),
   "foo   bar   baz\nyada  yada  yada\n", 'right result';
+is tablify([['a', '', 'b'], ['c', '', 'd']]), "a    b\nc    d\n",
+  'right result';
 
 # deprecated
 {
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use IO::Socket::INET;
@@ -57,7 +54,7 @@ websocket '/socket' => sub {
       $c->send(Mojo::IOLoop->stream($c->tx->connection)->timeout);
       $c->finish(1000 => 'I ♥ Mojolicious!');
     }
-  );
+  )->rendered(101);
 };
 
 websocket '/early_start' => sub {
@@ -134,7 +131,7 @@ websocket '/timeout' => sub {
 my $ua  = app->ua;
 my $res = $ua->get('/link')->success;
 is $res->code, 200, 'right status';
-like $res->body, qr!ws://localhost:\d+/!, 'right content';
+like $res->body, qr!ws://127\.0\.0\.1:\d+/!, 'right content';
 
 # Plain HTTP request
 $res = $ua->get('/socket')->res;
@@ -161,7 +158,7 @@ $ua->websocket(
 Mojo::IOLoop->start;
 Mojo::IOLoop->one_tick until exists $stash->{finished};
 is $stash->{finished}, 1, 'finish event has been emitted once';
-like $result, qr!test1test2ws://localhost:\d+/!, 'right result';
+like $result, qr!test1test2ws://127\.0\.0\.1:\d+/!, 'right result';
 
 # Failed WebSocket connection
 my ($code, $body, $ws);
@@ -176,7 +173,7 @@ $ua->websocket(
 );
 Mojo::IOLoop->start;
 ok !$ws, 'not a WebSocket';
-is $code, 426, 'right status';
+is $code, 200, 'right status';
 ok $body =~ /^(\d+)failed!$/, 'right content';
 is $1, 15, 'right timeout';
 
@@ -382,7 +379,7 @@ $ua->websocket(
 );
 Mojo::IOLoop->start;
 ok $finished, 'transaction is finished';
-ok !$ws, 'no websocket';
+ok !$ws, 'not a websocket';
 is $code, 500, 'right status';
 is $msg, 'Internal Server Error', 'right message';
 
@@ -398,7 +395,7 @@ $ua->websocket(
   }
 );
 Mojo::IOLoop->start;
-ok !$ws, 'no websocket';
+ok !$ws, 'not a websocket';
 is $code, 403,            'right status';
 is $msg,  "i'm a teapot", 'right message';
 
@@ -420,7 +417,7 @@ $ua->websocket(
 Mojo::IOLoop->start;
 is $status, 1006, 'right status';
 
-# 16bit length
+# 16-bit length
 $result = undef;
 $ua->websocket(
   '/echo' => sub {
@@ -462,10 +459,12 @@ $ua->websocket(
     $tx->on(
       frame => sub {
         my ($tx, $frame) = @_;
-        $pong = $frame->[5] if $frame->[4] == 10;
-        Mojo::IOLoop->stop;
+        return unless $frame->[4] == 10;
+        $pong = $frame->[5];
+        $tx->finish;
       }
     );
+    $tx->on(finish => sub { Mojo::IOLoop->stop });
     $tx->send([1, 0, 0, 0, 9, 'test']);
   }
 );
@@ -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');
@@ -140,7 +153,7 @@ is $frame->[4], 2,   'binary frame';
 is $frame->[5], 'a', 'right payload';
 is $bytes = $ws->build_frame(1, 0, 0, 0, 2, 'a'), $bytes, 'frames are equal';
 
-# 16bit text frame roundtrip
+# 16-bit text frame roundtrip
 $ws = Mojo::Transaction::WebSocket->new;
 $bytes = $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 10000);
 is $bytes, "\x81\x7e\x4e\x20" . ("\x68\x69" x 10000), 'right frame';
@@ -153,7 +166,7 @@ is $frame->[4], 1, 'text frame';
 is $frame->[5], 'hi' x 10000, 'right payload';
 is $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 10000), $bytes, 'frames are equal';
 
-# 64bit text frame roundtrip
+# 64-bit text frame roundtrip
 $ws = Mojo::Transaction::WebSocket->new(max_websocket_size => 500000);
 $bytes = $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 200000);
 is $bytes, "\x81\x7f\x00\x00\x00\x00\x00\x06\x1a\x80" . ("\x68\x69" x 200000),
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -40,7 +37,7 @@ websocket '/test' => sub {
 my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
 my $daemon = Mojo::Server::Daemon->new(app => app, silent => 1);
 $daemon->listen(['http://127.0.0.1'])->start;
-my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->port;
 
 # CONNECT proxy server for testing
 my (%buffer, $connected, $read, $sent);
@@ -118,23 +115,23 @@ my $id = Mojo::IOLoop->server(
     );
   }
 );
-my $proxy = Mojo::IOLoop->acceptor($id)->handle->sockport;
+my $proxy = Mojo::IOLoop->acceptor($id)->port;
 
 # Normal non-blocking request
 my $result;
 $ua->get(
-  "http://localhost:$port/" => sub {
+  "http://127.0.0.1:$port/" => sub {
     $result = pop->res->body;
     Mojo::IOLoop->stop;
   }
 );
 Mojo::IOLoop->start;
-is $result, "Hello World! / http://localhost:$port/", 'right content';
+is $result, "Hello World! / http://127.0.0.1:$port/", 'right content';
 
 # Normal WebSocket
 $result = undef;
 $ua->websocket(
-  "ws://localhost:$port/test" => sub {
+  "ws://127.0.0.1:$port/test" => sub {
     my ($ua, $tx) = @_;
     $tx->on(finish => sub { Mojo::IOLoop->stop });
     $tx->on(
@@ -151,7 +148,7 @@ Mojo::IOLoop->start;
 is $result, 'test1test2', 'right result';
 
 # Non-blocking proxy request
-$ua->proxy->http("http://localhost:$port");
+$ua->proxy->http("http://127.0.0.1:$port");
 my $kept_alive;
 $result = undef;
 $ua->get(
@@ -169,7 +166,7 @@ is $result, 'http://example.com/proxy', 'right content';
 # Kept alive proxy WebSocket
 ($kept_alive, $result) = ();
 $ua->websocket(
-  "ws://localhost:$port/test" => sub {
+  "ws://127.0.0.1:$port/test" => sub {
     my ($ua, $tx) = @_;
     $kept_alive = $tx->kept_alive;
     $tx->on(finish => sub { Mojo::IOLoop->stop });
@@ -194,10 +191,10 @@ is $tx->res->body, 'http://example.com/proxy', 'right content';
 
 # Proxy WebSocket
 $ua = Mojo::UserAgent->new;
-$ua->proxy->http("http://localhost:$proxy");
+$ua->proxy->http("http://127.0.0.1:$proxy");
 $result = undef;
 $ua->websocket(
-  "ws://localhost:$port/test" => sub {
+  "ws://127.0.0.1:$port/test" => sub {
     my ($ua, $tx) = @_;
     $tx->on(finish => sub { Mojo::IOLoop->stop });
     $tx->on(
@@ -211,17 +208,17 @@ $ua->websocket(
   }
 );
 Mojo::IOLoop->start;
-is $connected, "localhost:$port", 'connected';
+is $connected, "127.0.0.1:$port", 'connected';
 is $result,    'test1test2',      'right result';
 ok $read > 25, 'read enough';
 ok $sent > 25, 'sent enough';
 
 # Proxy WebSocket with bad target
-$ua->proxy->http("http://localhost:$proxy");
+$ua->proxy->http("http://127.0.0.1:$proxy");
 my $port2 = $port + 1;
 my ($success, $err);
 $ua->websocket(
-  "ws://localhost:$port2/test" => sub {
+  "ws://127.0.0.1:$port2/test" => sub {
     my ($ua, $tx) = @_;
     $success = $tx->success;
     $err     = $tx->error;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -59,7 +56,7 @@ my $listen
   . '&key=t/mojo/certs/server.key'
   . '&ca=t/mojo/certs/ca.crt';
 $daemon->listen([$listen])->start;
-my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->port;
 
 # Connect proxy server for testing
 my (%buffer, $connected, $read, $sent);
@@ -137,7 +134,20 @@ my $id = Mojo::IOLoop->server(
     );
   }
 );
-my $proxy = Mojo::IOLoop->acceptor($id)->handle->sockport;
+my $proxy = Mojo::IOLoop->acceptor($id)->port;
+
+# 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)->port;
+
+# Fake server to test idle connection
+$id = Mojo::IOLoop->server(sub { });
+my $idle = Mojo::IOLoop->acceptor($id)->port;
 
 # User agent with valid certificates
 my $ua = Mojo::UserAgent->new(
@@ -150,13 +160,13 @@ my $ua = Mojo::UserAgent->new(
 # Normal non-blocking request
 my $result;
 $ua->get(
-  "https://localhost:$port/" => sub {
+  "https://127.0.0.1:$port/" => sub {
     $result = pop->res->body;
     Mojo::IOLoop->stop;
   }
 );
 Mojo::IOLoop->start;
-is $result, "Hello World! / https://localhost:$port/", 'right content';
+is $result, "Hello World! / https://127.0.0.1:$port/", 'right content';
 
 # Broken redirect
 my $start;
@@ -169,7 +179,7 @@ $ua->on(
 $result = undef;
 my $works;
 $ua->max_redirects(3)->get(
-  "https://localhost:$port/broken_redirect" => sub {
+  "https://127.0.0.1:$port/broken_redirect" => sub {
     my ($ua, $tx) = @_;
     $result = $tx->res->body;
     $works  = $tx->res->headers->header('X-Works');
@@ -177,7 +187,7 @@ $ua->max_redirects(3)->get(
   }
 );
 Mojo::IOLoop->start;
-is $result, "Hello World! / https://localhost:$port/", 'right content';
+is $result, "Hello World! / https://127.0.0.1:$port/", 'right content';
 is $works,  'it does!',                                'right header';
 is $start,  2,                                         'redirected once';
 $ua->unsubscribe('start');
@@ -185,7 +195,7 @@ $ua->unsubscribe('start');
 # Normal WebSocket
 $result = undef;
 $ua->websocket(
-  "wss://localhost:$port/test" => sub {
+  "wss://127.0.0.1:$port/test" => sub {
     my ($ua, $tx) = @_;
     $tx->on(finish => sub { Mojo::IOLoop->stop });
     $tx->on(
@@ -202,11 +212,11 @@ Mojo::IOLoop->start;
 is $result, 'test1test2', 'right result';
 
 # Non-blocking proxy request
-$ua->proxy->https("http://sri:secr3t\@localhost:$proxy");
+$ua->proxy->https("http://sri:secr3t\@127.0.0.1:$proxy");
 $result = undef;
 my ($auth, $kept_alive);
 $ua->get(
-  "https://localhost:$port/proxy" => sub {
+  "https://127.0.0.1:$port/proxy" => sub {
     my ($ua, $tx) = @_;
     $result     = $tx->res->body;
     $auth       = $tx->req->headers->proxy_authorization;
@@ -217,12 +227,12 @@ $ua->get(
 Mojo::IOLoop->start;
 ok !$auth,       'no "Proxy-Authorization" header';
 ok !$kept_alive, 'connection was not kept alive';
-is $result, "https://localhost:$port/proxy", 'right content';
+is $result, "https://127.0.0.1:$port/proxy", 'right content';
 
 # Non-blocking kept alive proxy request
 ($kept_alive, $result) = ();
 $ua->get(
-  "https://localhost:$port/proxy" => sub {
+  "https://127.0.0.1:$port/proxy" => sub {
     my ($ua, $tx) = @_;
     $kept_alive = $tx->kept_alive;
     $result     = $tx->res->body;
@@ -230,14 +240,14 @@ $ua->get(
   }
 );
 Mojo::IOLoop->start;
-is $result, "https://localhost:$port/proxy", 'right content';
+is $result, "https://127.0.0.1:$port/proxy", 'right content';
 ok $kept_alive, 'connection was kept alive';
 
 # Kept alive proxy WebSocket
-$ua->proxy->https("http://localhost:$proxy");
+$ua->proxy->https("http://127.0.0.1:$proxy");
 ($kept_alive, $result) = ();
 $ua->websocket(
-  "wss://localhost:$port/test" => sub {
+  "wss://127.0.0.1:$port/test" => sub {
     my ($ua, $tx) = @_;
     $kept_alive = $tx->kept_alive;
     $tx->on(finish => sub { Mojo::IOLoop->stop });
@@ -253,26 +263,26 @@ $ua->websocket(
 );
 Mojo::IOLoop->start;
 ok $kept_alive, 'connection was kept alive';
-is $connected,  "localhost:$port", 'connected';
+is $connected,  "127.0.0.1:$port", 'connected';
 is $result,     'test1test2', 'right result';
 ok $read > 25, 'read enough';
 ok $sent > 25, 'sent enough';
 
 # Blocking proxy request
-$ua->proxy->https("http://sri:secr3t\@localhost:$proxy");
-my $tx = $ua->get("https://localhost:$port/proxy");
+$ua->proxy->https("http://sri:secr3t\@127.0.0.1:$proxy");
+my $tx = $ua->get("https://127.0.0.1:$port/proxy");
 is $tx->res->code, 200, 'right status';
-is $tx->res->body, "https://localhost:$port/proxy", 'right content';
+is $tx->res->body, "https://127.0.0.1:$port/proxy", 'right content';
 
 # Proxy WebSocket with bad target
-$ua->proxy->https("http://localhost:$proxy");
+$ua->proxy->https("http://127.0.0.1:$proxy");
 my $port2 = $port + 1;
 my ($success, $err);
 $ua->websocket(
-  "wss://localhost:$port2/test" => sub {
+  "wss://127.0.0.1:$port2/test" => sub {
     my ($ua, $tx) = @_;
     $success = $tx->success;
-    $err     = $tx->error;
+    $err     = $tx->res->error;
     Mojo::IOLoop->stop;
   }
 );
@@ -280,10 +290,20 @@ Mojo::IOLoop->start;
 ok !$success, 'no success';
 is $err->{message}, 'Proxy connection failed', 'right error';
 
+# Failed TLS handshake through proxy
+$tx = $ua->get("https://127.0.0.1:$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://127.0.0.1:$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");
+$tx = $ua->get("https://127.0.0.1:$port/proxy");
 is $tx->res->code, 200, 'right status';
-is $tx->res->body, "https://localhost:$port/proxy", 'right content';
+is $tx->res->body, "https://127.0.0.1:$port/proxy", 'right content';
 
 done_testing();
@@ -3,7 +3,6 @@ use Mojo::Base -strict;
 BEGIN {
   $ENV{PLACK_ENV}    = undef;
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -58,6 +57,7 @@ is_deeply $t->app->commands->namespaces,
 is $t->app, $t->app->commands->app, 'applications are equal';
 is $t->app->static->file('hello.txt')->slurp,
   "Hello Mojo from a development static file!\n", 'right content';
+is $t->app->static->file('does_not_exist.html'), undef, 'no file';
 is $t->app->moniker, 'mojolicious_test', 'right moniker';
 is $t->app->secrets->[0], $t->app->moniker, 'secret defaults to moniker';
 is $t->app->renderer->template_handler(
@@ -65,6 +65,8 @@ is $t->app->renderer->template_handler(
 is $t->app->build_controller->req->url, '', 'no URL';
 is $t->app->build_controller->render_to_string('does_not_exist'), undef,
   'no result';
+is $t->app->build_controller->render_to_string(inline => '%= $c', c => 'foo'),
+  "foo\n", 'right result';
 
 # Missing methods and functions (AUTOLOAD)
 eval { $t->app->missing };
@@ -91,45 +93,47 @@ like $@, qr/^Undefined subroutine &Mojolicious::Route::missing called/,
 # Hidden controller attributes and methods
 $t->app->routes->hide('bar');
 ok !$t->app->routes->is_hidden('foo'), 'not hidden';
-ok $t->app->routes->is_hidden('bar'),              'is hidden';
-ok $t->app->routes->is_hidden('_foo'),             'is hidden';
-ok $t->app->routes->is_hidden('AUTOLOAD'),         'is hidden';
-ok $t->app->routes->is_hidden('DESTROY'),          'is hidden';
-ok $t->app->routes->is_hidden('FOO_BAR'),          'is hidden';
-ok $t->app->routes->is_hidden('app'),              'is hidden';
-ok $t->app->routes->is_hidden('attr'),             'is hidden';
-ok $t->app->routes->is_hidden('continue'),         'is hidden';
-ok $t->app->routes->is_hidden('cookie'),           'is hidden';
-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('match'),            'is hidden';
-ok $t->app->routes->is_hidden('new'),              'is hidden';
-ok $t->app->routes->is_hidden('on'),               'is hidden';
-ok $t->app->routes->is_hidden('param'),            'is hidden';
-ok $t->app->routes->is_hidden('redirect_to'),      'is hidden';
-ok $t->app->routes->is_hidden('render'),           'is hidden';
-ok $t->app->routes->is_hidden('render_exception'), 'is hidden';
-ok $t->app->routes->is_hidden('render_later'),     'is hidden';
-ok $t->app->routes->is_hidden('render_maybe'),     'is hidden';
-ok $t->app->routes->is_hidden('render_not_found'), 'is hidden';
-ok $t->app->routes->is_hidden('render_static'),    'is hidden';
-ok $t->app->routes->is_hidden('render_to_string'), 'is hidden';
-ok $t->app->routes->is_hidden('rendered'),         'is hidden';
-ok $t->app->routes->is_hidden('req'),              'is hidden';
-ok $t->app->routes->is_hidden('res'),              'is hidden';
-ok $t->app->routes->is_hidden('respond_to'),       'is hidden';
-ok $t->app->routes->is_hidden('send'),             'is hidden';
-ok $t->app->routes->is_hidden('session'),          'is hidden';
-ok $t->app->routes->is_hidden('signed_cookie'),    'is hidden';
-ok $t->app->routes->is_hidden('stash'),            'is hidden';
-ok $t->app->routes->is_hidden('tap'),              'is hidden';
-ok $t->app->routes->is_hidden('tx'),               'is hidden';
-ok $t->app->routes->is_hidden('url_for'),          'is hidden';
-ok $t->app->routes->is_hidden('validation'),       'is hidden';
-ok $t->app->routes->is_hidden('write'),            'is hidden';
-ok $t->app->routes->is_hidden('write_chunk'),      'is hidden';
+ok $t->app->routes->is_hidden('bar'),                 'is hidden';
+ok $t->app->routes->is_hidden('_foo'),                'is hidden';
+ok $t->app->routes->is_hidden('AUTOLOAD'),            'is hidden';
+ok $t->app->routes->is_hidden('DESTROY'),             'is hidden';
+ok $t->app->routes->is_hidden('FOO_BAR'),             'is hidden';
+ok $t->app->routes->is_hidden('app'),                 'is hidden';
+ok $t->app->routes->is_hidden('attr'),                'is hidden';
+ok $t->app->routes->is_hidden('continue'),            'is hidden';
+ok $t->app->routes->is_hidden('cookie'),              'is hidden';
+ok $t->app->routes->is_hidden('every_cookie'),        'is hidden';
+ok $t->app->routes->is_hidden('every_param'),         'is hidden';
+ok $t->app->routes->is_hidden('every_signed_cookie'), 'is hidden';
+ok $t->app->routes->is_hidden('finish'),              'is hidden';
+ok $t->app->routes->is_hidden('flash'),               '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';
+ok $t->app->routes->is_hidden('param'),               'is hidden';
+ok $t->app->routes->is_hidden('redirect_to'),         'is hidden';
+ok $t->app->routes->is_hidden('render'),              'is hidden';
+ok $t->app->routes->is_hidden('render_exception'),    'is hidden';
+ok $t->app->routes->is_hidden('render_later'),        'is hidden';
+ok $t->app->routes->is_hidden('render_maybe'),        'is hidden';
+ok $t->app->routes->is_hidden('render_not_found'),    'is hidden';
+ok $t->app->routes->is_hidden('render_to_string'),    'is hidden';
+ok $t->app->routes->is_hidden('rendered'),            'is hidden';
+ok $t->app->routes->is_hidden('req'),                 'is hidden';
+ok $t->app->routes->is_hidden('res'),                 'is hidden';
+ok $t->app->routes->is_hidden('respond_to'),          'is hidden';
+ok $t->app->routes->is_hidden('send'),                'is hidden';
+ok $t->app->routes->is_hidden('session'),             'is hidden';
+ok $t->app->routes->is_hidden('signed_cookie'),       'is hidden';
+ok $t->app->routes->is_hidden('stash'),               'is hidden';
+ok $t->app->routes->is_hidden('tap'),                 'is hidden';
+ok $t->app->routes->is_hidden('tx'),                  'is hidden';
+ok $t->app->routes->is_hidden('url_for'),             'is hidden';
+ok $t->app->routes->is_hidden('validation'),          'is hidden';
+ok $t->app->routes->is_hidden('write'),               'is hidden';
+ok $t->app->routes->is_hidden('write_chunk'),         'is hidden';
 
 # Unknown hooks
 ok !$t->app->plugins->emit_chain('does_not_exist'), 'hook has been emitted';
@@ -224,8 +228,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 +262,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 +383,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 +399,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)')
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -10,7 +10,14 @@ use Test::More;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
-use Mojolicious::Commands;
+# Make sure @ARGV is not changed
+{
+  local $ENV{MOJO_MODE};
+  local @ARGV = qw(-m production -x whatever);
+  require Mojolicious::Commands;
+  is $ENV{MOJO_MODE}, 'production', 'right mode';
+  is_deeply \@ARGV, [qw(-m production -x whatever)], 'unchanged';
+}
 
 # Environment detection
 my $commands = Mojolicious::Commands->new;
@@ -55,6 +62,16 @@ is $app->start('test_command'), 'works!', 'right result';
     'works!', 'right result');
 }
 
+# Do not pick up options for detected environments
+{
+  local $ENV{MOJO_MODE};
+  local $ENV{PLACK_ENV} = 'testing';
+  local @ARGV = qw(psgi -m production);
+  is ref Mojolicious::Commands->start_app('MojoliciousTest'), 'CODE',
+    'right reference';
+  is $ENV{MOJO_MODE}, undef, 'no mode';
+}
+
 # mojo
 ok $commands->description, 'has a description';
 like $commands->message,   qr/COMMAND/, 'has a message';
@@ -70,14 +70,15 @@ is_deeply $stash, {a => 1, b => 2}, 'set via hash reference';
 is $c->param('foo'), undef, 'no value';
 is $c->param(foo => 'works')->param('foo'), 'works', 'right value';
 is $c->param(foo => 'too')->param('foo'),   'too',   'right value';
-is $c->param(foo => qw(just works))->param('foo'), 'just', 'right value';
-is_deeply [$c->param('foo')], [qw(just works)], 'right values';
+is $c->param(foo => qw(just works))->param('foo'), 'works', 'right value';
+is_deeply $c->every_param('foo'), [qw(just works)], 'right values';
+is_deeply $c->every_param('bar'), [], 'no values';
 is $c->param(foo => undef)->param('foo'), undef, 'no value';
 is $c->param(foo => Mojo::Upload->new(name => 'bar'))->param('foo')->name,
   'bar', 'right value';
-is scalar $c->param(foo => ['ba;r', 'baz'])->param('foo'), 'ba;r',
+is scalar $c->param(foo => ['ba;r', 'baz'])->param('foo'), 'baz',
   'right value';
-is_deeply [$c->param('foo')], ['ba;r', 'baz'], 'right values';
+is_deeply $c->every_param('foo'), ['ba;r', 'baz'], 'right values';
 
 # Reserved stash values are hidden
 $c = Mojolicious::Controller->new;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -21,6 +20,13 @@ plugin(Mount => ('/x/♥' => $external));
 plugin Mount => {'MOJOLICIO.US/' => $external};
 plugin(Mount => ('*.foo-bar.de/♥/123' => $external));
 
+# Make sure session can be modified from both apps
+hook before_routes => sub {
+  my $c = shift;
+  return unless $c->req->url->path->contains('/x/1/secondary');
+  $c->session->{secondary} += 10;
+};
+
 get '/hello' => 'works';
 
 get '/primary' => sub {
@@ -40,13 +46,13 @@ $t->get_ok('/primary')->status_is(200)->content_is(1);
 $t->get_ok('/primary')->status_is(200)->content_is(2);
 
 # Session in external app
-$t->get_ok('/x/1/secondary')->status_is(200)->content_is(1);
+$t->get_ok('/x/1/secondary')->status_is(200)->content_is(11);
 
 # Session again
 $t->get_ok('/primary')->status_is(200)->content_is(3);
 
 # Session in external app again
-$t->get_ok('/x/1/secondary')->status_is(200)->content_is(2);
+$t->get_ok('/x/1/secondary')->status_is(200)->content_is(22);
 
 # External app
 $t->get_ok('/x/1')->status_is(200)->content_is('too%21');
@@ -58,14 +64,14 @@ $t->get_ok('/x/1/index.html')->status_is(200)
 # External app with different prefix
 $t->get_ok('/x/1/test')->status_is(200)->content_is('works%21');
 
-# External app with unicode prefix
+# External app with Unicode prefix
 $t->get_ok('/x/♥')->status_is(200)->content_is('too%21');
 
-# Static file from external app with unicode prefix
+# Static file from external app with Unicode prefix
 $t->get_ok('/x/♥/index.html')->status_is(200)
   ->content_is("External static file!\n");
 
-# External app with unicode prefix again
+# External app with Unicode prefix again
 $t->get_ok('/x/♥/test')->status_is(200)->content_is('works%21');
 
 # External app with domain
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -194,7 +193,7 @@ $t->get_ok('/x/1/stream')->status_is(200)->content_is('hello!');
 
 # URL from myapp.pl
 $t->get_ok('/x/1/url/☃')->status_is(200)
-  ->content_is('/x/1/url/%E2%98%83 -> /x/1/%E2%98%83/stream!');
+  ->content_is('/x/1/url/%E2%98%83.json -> /x/1/%E2%98%83/stream!');
 
 # Route to template from myapp.pl
 $t->get_ok('/x/1/template/menubar')->status_is(200)
@@ -203,7 +202,7 @@ $t->get_ok('/x/1/template/menubar')->status_is(200)
 # Missing template from myapp.pl
 $t->get_ok('/x/1/template/does_not_exist')->status_is(404);
 
-# Template from myapp.pl with unicode prefix
+# Template from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/')->status_is(200)->content_is(<<'EOF');
 myapp
 works ♥!Insecure!Insecure!
@@ -215,25 +214,26 @@ too!works!!!Mojolicious::Plugin::Config::Sandbox
 </form>
 EOF
 
-# Static file from myapp.pl with unicode prefix
+# Static file from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/index.html')->status_is(200)
   ->content_is("External static file!\n");
 
-# Echo from myapp.pl with unicode prefix
+# Echo from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/echo')->status_is(200)->content_is('echo: works 2!');
 
-# Stream from myapp.pl with unicode prefix
+# Stream from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/stream')->status_is(200)->content_is('hello!');
 
-# URL from myapp.pl with unicode prefix
+# URL from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/url/☃')->status_is(200)
-  ->content_is('/x/%E2%99%A5/url/%E2%98%83 -> /x/%E2%99%A5/%E2%98%83/stream!');
+  ->content_is(
+  '/x/%E2%99%A5/url/%E2%98%83.json -> /x/%E2%99%A5/%E2%98%83/stream!');
 
-# Route to template from myapp.pl with unicode prefix
+# Route to template from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/template/menubar')->status_is(200)
   ->content_is("myapp\nworks ♥!Insecure!Insecure!\n");
 
-# Missing template from myapp.pl with unicode prefix
+# Missing template from myapp.pl with Unicode prefix
 $t->get_ok('/x/♥/template/does_not_exist')->status_is(404);
 
 # A little bit of everything from myapp2.pl
@@ -253,17 +253,17 @@ $t->get_ok('/y/1/cached?cache=fail')->status_is(200)->content_is('foo');
 # 404 from myapp2.pl
 $t->get_ok('/y/1/2')->status_is(404);
 
-# myapp2.pl with unicode prefix
+# myapp2.pl with Unicode prefix
 $t->get_ok('/y/♥')->status_is(200)
   ->content_is("myapp2\nworks 3!\nInsecure too!");
 
-# Caching helper from myapp2.pl with unicode prefix
+# Caching helper from myapp2.pl with Unicode prefix
 $t->get_ok('/y/♥/cached?cache=bar')->status_is(200)->content_is('bar');
 
-# Caching helper with cached value from myapp2.pl with unicode prefix
+# Caching helper with cached value from myapp2.pl with Unicode prefix
 $t->get_ok('/y/♥/cached?cache=fail')->status_is(200)->content_is('bar');
 
-# 404 from myapp2.pl with unicode prefix
+# 404 from myapp2.pl with Unicode prefix
 $t->get_ok('/y/♥/2')->status_is(404);
 
 # main
@@ -328,7 +328,7 @@ $t->get_ok('/host' => {Host => 'www.example.com'})->status_is(200)
 $t->get_ok('/host' => {Host => 'foo.bar.example.com'})->status_is(200)
   ->header_is('X-Message' => 'it works!')->content_is('foo.bar.example.com');
 
-# Template from myapp.pl with wildcard domain and unicode prefix
+# Template from myapp.pl with wildcard domain and Unicode prefix
 $t->get_ok('/♥/123/' => {Host => 'foo-bar.de'})->status_is(200)
   ->content_is(<<'EOF');
 myapp
@@ -341,27 +341,27 @@ too!works!!!Mojolicious::Plugin::Config::Sandbox
 </form>
 EOF
 
-# Host from myapp.pl with wildcard domain and unicode prefix
+# Host from myapp.pl with wildcard domain and Unicode prefix
 $t->get_ok('/♥/123/host' => {Host => 'foo-bar.de'})->status_is(200)
   ->header_is('X-Message' => 'it works!')->content_is('foo-bar.de');
 
-# Echo from myapp.pl with wildcard domain and unicode prefix
+# Echo from myapp.pl with wildcard domain and Unicode prefix
 $t->get_ok('/♥/123/echo' => {Host => 'foo-bar.de'})->status_is(200)
   ->content_is('echo: works 3!');
 
-# Host from myapp.pl with wildcard domain and unicode prefix again
+# Host from myapp.pl with wildcard domain and Unicode prefix again
 $t->get_ok('/♥/123/host' => {Host => 'www.foo-bar.de'})->status_is(200)
   ->header_is('X-Message' => 'it works!')->content_is('www.foo-bar.de');
 
-# Host from myapp.pl with wildcard domain and unicode prefix again
+# Host from myapp.pl with wildcard domain and Unicode prefix again
 $t->get_ok('/♥/123/echo' => {Host => 'www.foo-bar.de'})->status_is(200)
   ->content_is('echo: works 3!');
 
-# Text from myapp.pl with wildcard domain and unicode prefix
+# Text from myapp.pl with wildcard domain and Unicode prefix
 $t->get_ok('/♥/123/one' => {Host => 'www.foo-bar.de'})->status_is(200)
   ->content_is('One');
 
-# Another text from myapp.pl with wildcard domain and unicode prefix
+# Another text from myapp.pl with wildcard domain and Unicode prefix
 $t->get_ok('/♥/123/one/two' => {Host => 'www.foo-bar.de'})->status_is(200)
   ->content_is('Two');
 
@@ -371,6 +371,13 @@ $t->get_ok('/' => {Host => 'mojoliciousxorg'})->status_is(404);
 # Another invalid domain
 $t->get_ok('/' => {Host => 'www.kraihxcom'})->status_is(404);
 
+# Embedded WebSocket
+$t->websocket_ok('/x/♥/url_for')->send_ok('ws_test')
+  ->message_ok->message_like(qr!^ws://127\.0\.0\.1:\d+/x/%E2%99%A5/url_for$!)
+  ->send_ok('index')
+  ->message_ok->message_like(qr!^http://127\.0\.0\.1:\d+/x/%E2%99%A5$!)
+  ->finish_ok;
+
 done_testing();
 
 __DATA__
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -37,7 +37,7 @@ get '/stream' => sub {
 
 get '/url/☃' => sub {
   my $c     = shift;
-  my $route = $c->url_for;
+  my $route = $c->url_for({format => 'json'});
   my $rel   = $c->url_for('/☃/stream');
   $c->render(text => "$route -> $rel!");
 };
@@ -53,6 +53,16 @@ get '/one/two' => {text => 'Two'};
 
 get '/template/:template';
 
+websocket '/url_for' => sub {
+  my $c = shift;
+  $c->on(
+    message => sub {
+      my ($c, $msg) = @_;
+      $c->send($c->url_for($msg)->to_abs);
+    }
+  );
+} => 'ws_test';
+
 app->start;
 __DATA__
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -39,6 +38,6 @@ $t->get_ok('/stream')->status_is(200)->content_is('hello!');
 
 # URL generated by myapp.pl
 $t->get_ok('/url/☃')->status_is(200)
-  ->content_is('/url/%E2%98%83 -> /%E2%98%83/stream!');
+  ->content_is('/url/%E2%98%83.json -> /%E2%98%83/stream!');
 
 done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -206,7 +203,7 @@ $t->get_ok('/multi')->status_is(200)
 # Multiple cookies with same name (again)
 $t->get_ok('/multi')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
-  ->content_is("one\nthree\none\ntwo\nfour\nsix\nfour\nfive\n");
+  ->content_is("two\nthree\none\ntwo\nfive\nsix\nfour\nfive\n");
 
 # Missing action behind bridge
 $t->get_ok('/missing')->status_is(404)->content_is("Oops!\n");
@@ -274,9 +271,8 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/bridge2stash')->status_is(200)
   ->content_is(
   "stash too!cookie!!signed_cookie!!bad_cookie--12345678!session!flash!\n");
-like $log, qr/Cookie "foo" not signed\./, 'right message';
-like $log, qr/Bad signed cookie "bad", possible hacking attempt\./,
-  'right message';
+like $log, qr/Cookie "foo" not signed\./,        'right message';
+like $log, qr/Cookie "bad" has bad signature\./, 'right message';
 ok $t->tx->res->cookie('mojolicious')->httponly,
   'session cookie has HttpOnly flag';
 $t->app->log->unsubscribe(message => $cb);
@@ -288,15 +284,15 @@ my $hmac    = $session->clone->hmac_sha1_sum($t->app->secrets->[0]);
 $t->get_ok('/bridge2stash' => {Cookie => "mojolicious=$session--$hmac"})
   ->status_is(200)->content_is("stash too!!!!!!!!\n");
 
-# Without cookie jar
-$t->ua->cookie_jar(0);
+# Not extracting cookies
+$t->reset_session->ua->cookie_jar->extracting(0);
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
   ->content_is("stash too!!!!!!!!\n");
 
-# Again without cookie jar
+# Still not extracting cookies
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
   ->content_is("stash too!!!!!!!!\n");
-$t->reset_session->ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
+$t->ua->cookie_jar->extracting(1);
 
 # Fresh start without cookies, session or flash
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
@@ -474,15 +470,15 @@ Oops!
 % my ($one, $three) = $c->cookie([qw(unsigned1 unsigned2)]);
 %= $one // ''
 %= $three // '';
-% my @unsigned1 = $c->cookie('unsigned1');
-%= $unsigned1[0] // ''
-%= $unsigned1[1] // ''
+% my $unsigned1 = $c->every_cookie('unsigned1');
+%= $unsigned1->[0] // ''
+%= $unsigned1->[1] // ''
 % my ($four, $six) = $c->signed_cookie([qw(signed1 signed2)]);
 %= $four // ''
 %= $six // '';
-% my @signed1 = $c->signed_cookie('signed1');
-%= $signed1[0] // ''
-%= $signed1[1] // ''
+% my $signed1 = $c->every_signed_cookie('signed1');
+%= $signed1->[0] // ''
+%= $signed1->[1] // ''
 
 @@ param_auth.html.epl
 Bender!
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -17,13 +16,17 @@ use Test::Mojo;
 app->config(it => 'works');
 is_deeply app->config, {it => 'works'}, 'right value';
 
+# Invalid config file
+my $path = abs_path catfile(dirname(__FILE__), 'public', 'hello.txt');
+eval { plugin JSONConfig => {file => $path}; };
+like $@, qr/Malformed JSON/, 'right error';
+
 # Load plugins
 my $config
   = plugin j_s_o_n_config => {default => {foo => 'baz', hello => 'there'}};
 my $log = '';
 my $cb = app->log->on(message => sub { $log .= pop });
-my $path
-  = abs_path(catfile(dirname(__FILE__), 'json_config_lite_app_abs.json'));
+$path = abs_path catfile(dirname(__FILE__), 'json_config_lite_app_abs.json');
 plugin JSONConfig => {file => $path};
 like $log, qr/Reading configuration file "\Q$path\E"\./, 'right message';
 app->log->unsubscribe(message => $cb);
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -16,9 +16,6 @@ sub startup {
   push @{$self->renderer->classes}, 'SingleFileTestApp::Foo';
   push @{$self->static->classes},   'SingleFileTestApp::Foo';
 
-  # Allow redispatching controller
-  push @{$self->routes->base_classes}, 'Mojo::Base';
-
   # Helper route
   $self->routes->route('/helper')->to(
     cb => sub {
@@ -32,7 +29,7 @@ sub startup {
 }
 
 package SingleFileTestApp::Redispatch;
-use Mojo::Base -base;
+use Mojo::Base 'Mojo';
 
 sub handler {
   my ($self, $c) = @_;
@@ -63,7 +60,7 @@ sub data_template { shift->render('index') }
 
 sub data_template2 { shift->stash(template => 'too') }
 
-sub data_static { shift->render_static('singlefiletestapp/foo.txt') }
+sub data_static { shift->reply->static('singlefiletestapp/foo.txt') }
 
 sub index {
   shift->stash(template => 'WithGreenLayout', msg => 'works great!');
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -38,6 +37,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!' });
 
@@ -91,8 +96,8 @@ any sub { shift->render(text => 'Bye!') };
 
 post '/multipart/form' => sub {
   my $c    = shift;
-  my @test = $c->param('test');
-  $c->render(text => join "\n", @test);
+  my $test = $c->every_param('test');
+  $c->render(text => join "\n", @$test);
 };
 
 get '/auto_name' => sub {
@@ -197,8 +202,9 @@ get '/root' => sub { shift->render(text => 'root fallback!') };
 get '/template.txt' => {template => 'template', format => 'txt'};
 
 get ':number' => [number => qr/0/] => sub {
-  my $c       = shift;
-  my $url     = $c->req->url->to_abs;
+  my $c   = shift;
+  my $url = $c->req->url->to_abs;
+  $c->res->headers->header('X-Original' => $c->tx->original_remote_address);
   my $address = $c->tx->remote_address;
   my $num     = $c->param('number');
   $c->render(text => "$url-$address-$num");
@@ -231,7 +237,7 @@ get '/source' => sub {
   my $c = shift;
   my $file = $c->param('fail') ? 'does_not_exist.txt' : '../lite_app.t';
   $c->render_maybe('this_does_not_ever_exist')
-    or $c->render_static($file)
+    or $c->reply->static($file)
     or $c->res->headers->header('X-Missing' => 1);
 };
 
@@ -369,24 +375,14 @@ get '/subrequest_non_blocking' => sub {
   $c->stash->{nb} = 'success!';
 };
 
-get '/redirect_url' => sub {
-  shift->redirect_to('http://127.0.0.1/foo')->render(text => 'Redirecting!');
-};
+get '/redirect_url' => sub { shift->redirect_to('http://127.0.0.1/foo') };
 
-get '/redirect_path' => sub {
-  shift->redirect_to('/foo/bar?foo=bar')->render(text => 'Redirecting!');
-};
+get '/redirect_path' => sub { shift->redirect_to('/foo/bar?foo=bar') };
 
-get '/redirect_named' => sub {
-  shift->redirect_to('index', format => 'txt')->render(text => 'Redirecting!');
-};
+get '/redirect_named' => sub { shift->redirect_to('index', format => 'txt') };
 
 get '/redirect_twice' => sub { shift->redirect_to('/redirect_named') };
 
-get '/redirect_no_render' => sub {
-  shift->redirect_to('index', {format => 'txt'});
-};
-
 get '/redirect_callback' => sub {
   my $c = shift;
   Mojo::IOLoop->next_tick(
@@ -398,7 +394,7 @@ get '/redirect_callback' => sub {
   );
 };
 
-get '/static_render' => sub { shift->render_static('hello.txt') };
+get '/static' => sub { shift->reply->static('hello.txt') };
 
 app->types->type('koi8-r' => 'text/html; charset=koi8-r');
 get '/koi8-r' => sub {
@@ -474,6 +470,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)
@@ -528,7 +526,7 @@ $t->get_ok('/alternatives/☃')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('/alternatives/%E2%98%83');
 
-# Different unicode alternative
+# Different Unicode alternative
 $t->get_ok('/alternatives/♥')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('/alternatives/%E2%99%A5');
@@ -600,7 +598,7 @@ $t->get_ok('/multi/B?foo=A&baz=C')->status_is(200)
 # Injection attack
 $t->get_ok('/multi/B?foo=A&foo=E&baz=C&yada=D&yada=text&yada=fail')
   ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
-  ->content_is('ABC');
+  ->content_is('EBC');
 
 # Missing parameter
 $t->get_ok('/multi/B?baz=C')->status_is(200)
@@ -690,7 +688,7 @@ $t->get_ok('/regex/in/template')->status_is(200)
 my $url = $t->ua->server->url->userinfo('sri:foo')->path('/stream')
   ->query(foo => 'bar');
 $t->get_ok($url)->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
-  ->content_like(qr!^foobarsri:foohttp://localhost:\d+/stream$!);
+  ->content_like(qr!^foobarsri:foohttp://127\.0\.0\.1:\d+/stream$!);
 
 # Not ajax
 $t->get_ok('/maybe/ajax')->status_is(200)
@@ -733,7 +731,8 @@ $t->get_ok('/.html')->status_is(200)
   local $ENV{MOJO_REVERSE_PROXY} = 1;
   $t->ua->server->restart;
   $t->get_ok('/0' => {'X-Forwarded-For' => '192.0.2.2, 192.0.2.1'})
-    ->status_is(200)->content_like(qr!http://localhost:\d+/0-192\.0\.2\.1-0$!);
+    ->status_is(200)->header_unlike('X-Original' => qr/192\.0\.2\.1/)
+    ->content_like(qr!http://127\.0\.0\.1:\d+/0-192\.0\.2\.1-0$!);
 }
 
 # Reverse proxy with "X-Forwarded-Proto"
@@ -741,19 +740,19 @@ $t->get_ok('/.html')->status_is(200)
   local $ENV{MOJO_REVERSE_PROXY} = 1;
   $t->ua->server->restart;
   $t->get_ok('/0' => {'X-Forwarded-Proto' => 'https'})->status_is(200)
-    ->content_like(qr!^https://localhost:\d+/0-!)->content_like(qr/-0$/)
+    ->content_like(qr!^https://127\.0\.0\.1:\d+/0-!)->content_like(qr/-0$/)
     ->content_unlike(qr!-192\.0\.2\.1-0$!);
 }
 
 # "X-Forwarded-For"
 $t->ua->server->restart;
 $t->get_ok('/0' => {'X-Forwarded-For' => '192.0.2.2, 192.0.2.1'})
-  ->status_is(200)->content_like(qr!^http://localhost:\d+/0-!)
+  ->status_is(200)->content_like(qr!^http://127\.0\.0\.1:\d+/0-!)
   ->content_like(qr/-0$/)->content_unlike(qr!-192\.0\.2\.1-0$!);
 
 # "X-Forwarded-Proto"
 $t->get_ok('/0' => {'X-Forwarded-Proto' => 'https'})->status_is(200)
-  ->content_like(qr!^http://localhost:\d+/0-!)->content_like(qr/-0$/)
+  ->content_like(qr!^http://127\.0\.0\.1:\d+/0-!)->content_like(qr/-0$/)
   ->content_unlike(qr!-192\.0\.2\.1-0$!);
 
 # Inline "epl" template
@@ -789,10 +788,10 @@ $t->app->log->unsubscribe(message => $cb);
 # With body and max message size
 {
   local $ENV{MOJO_MAX_MESSAGE_SIZE} = 1024;
-  $t->get_ok('/', '1234' x 1024)->status_is(413)
+  $t->get_ok('/', '1234' x 1024)->status_is(200)
     ->header_is(Connection => 'close')
     ->content_is(
-    "/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
+    "413\n/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
 }
 
 # Relaxed placeholder
@@ -965,22 +964,18 @@ is $nb, 'broken!', 'right text';
 
 # Redirect to URL
 $t->get_ok('/redirect_url')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_is(Location => 'http://127.0.0.1/foo')->content_is('Redirecting!');
+  ->header_is(Server   => 'Mojolicious (Perl)')
+  ->header_is(Location => 'http://127.0.0.1/foo')->content_is('');
 
 # Redirect to path
 $t->get_ok('/redirect_path')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_like(Location => qr!/foo/bar\?foo=bar$!)
-  ->content_is('Redirecting!');
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_like(Location => qr!/foo/bar\?foo=bar$!)->content_is('');
 
 # Redirect to named route
 $t->get_ok('/redirect_named')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_like(Location => qr!/template.txt$!)->content_is('Redirecting!');
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_like(Location => qr!/template.txt$!)->content_is('');
 
 # Redirect twice
 $t->ua->max_redirects(3);
@@ -993,12 +988,6 @@ is $redirects->[0]->req->url->path, '/redirect_twice', 'right path';
 is $redirects->[1]->req->url->path, '/redirect_named', 'right path';
 $t->ua->max_redirects(0);
 
-# Redirect without rendering
-$t->get_ok('/redirect_no_render')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 0)
-  ->header_like(Location => qr!/template.txt$!)->content_is('');
-
 # Non-blocking redirect
 $t->get_ok('/redirect_callback')->status_is(301)
   ->header_is(Server           => 'Mojolicious (Perl)')
@@ -1006,7 +995,7 @@ $t->get_ok('/redirect_callback')->status_is(301)
   ->header_is(Location => 'http://127.0.0.1/foo')->content_is('Whatever!');
 
 # Static file
-$t->get_ok('/static_render')->status_is(200)
+$t->get_ok('/static')->status_is(200)
   ->header_is(Server           => 'Mojolicious (Perl)')
   ->header_is('Content-Length' => 31)
   ->content_is("Hello Mojo from a static file!\n");
@@ -1022,7 +1011,7 @@ $t->get_ok('/redirect_named')->status_is(200)
 $t->ua->max_redirects(0);
 Test::Mojo->new->tx($t->tx->previous)->status_is(302)
   ->header_is(Server => 'Mojolicious (Perl)')
-  ->header_like(Location => qr!/template.txt$!)->content_is('Redirecting!');
+  ->header_like(Location => qr!/template.txt$!)->content_is('');
 
 # Request with koi8-r content
 my $koi8
@@ -1071,7 +1060,7 @@ http://mojolicio.us/test?foo=23&bar=24&baz=25
 /bar/23?bar=24&baz=25&foo=yada
 EOF
 $t->get_ok('/url_with/foo?foo=bar')->status_is(200)
-  ->content_like(qr!http://localhost:\d+/url_with/bar\?foo\=bar!);
+  ->content_like(qr!http://127\.0\.0\.1:\d+/url_with/bar\?foo\=bar!);
 
 # Dynamic inline template
 $t->get_ok('/dynamic/inline')->status_is(200)
@@ -1114,6 +1103,7 @@ Test ok!
 
 @@ root.html.epl
 % my $c = shift;
+<% if (my $err = $c->req->error) { =%><%= "$err->{advice}\n" %><% } =%>
 %== $c->url_for('root_path')
 %== $c->url_for('root_path')
 %== $c->url_for('root_path')
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -175,7 +172,7 @@ get '/longpoll/static/delayed' => sub {
   $c->on(finish => sub { shift->stash->{finished}++ });
   $c->cookie(bar => 'baz');
   $c->session(foo => 'bar');
-  Mojo::IOLoop->timer(0.25 => sub { $c->render_static('hello.txt') });
+  Mojo::IOLoop->timer(0.25 => sub { $c->reply->static('hello.txt') });
 };
 
 get '/longpoll/dynamic/delayed' => sub {
@@ -217,9 +214,8 @@ get '/too_long' => sub {
   my $c = shift;
   $c->res->code(200);
   $c->res->headers->content_type('text/plain');
-  $c->res->headers->content_length(12);
-  $c->write('how');
-  Mojo::IOLoop->timer(5 => sub { $c->write('dy plain!') });
+  $c->res->headers->content_length(17);
+  $c->write('Waiting forever!');
 };
 
 my $steps;
@@ -427,33 +423,14 @@ ok !$stash->{writing}, 'finish event timing is right';
 ok $stash->{destroyed}, 'controller has been destroyed';
 
 # Request timeout
-$tx = $t->ua->request_timeout(0.5)->build_tx(GET => '/too_long');
-$buffer = '';
-$tx->res->content->unsubscribe('read')->on(
-  read => sub {
-    my ($content, $chunk) = @_;
-    $buffer .= $chunk;
-  }
-);
-$t->ua->start($tx);
-is $tx->res->code, 200, 'right status';
+$tx = $t->ua->request_timeout(0.5)->get('/too_long');
 is $tx->error->{message}, 'Request timeout', 'right error';
-is $buffer, 'how', 'right content';
 $t->ua->request_timeout(0);
 
 # Inactivity timeout
-$tx = $t->ua->inactivity_timeout(0.5)->build_tx(GET => '/too_long');
-$buffer = '';
-$tx->res->content->unsubscribe('read')->on(
-  read => sub {
-    my ($content, $chunk) = @_;
-    $buffer .= $chunk;
-  }
-);
-$t->ua->start($tx);
-is $tx->res->code, 200, 'right status';
+$tx = $t->ua->inactivity_timeout(0.5)->get('/too_long');
 is $tx->error->{message}, 'Inactivity timeout', 'right error';
-is $buffer, 'how', 'right content';
+$t->ua->inactivity_timeout(20);
 
 # Transaction is available after rendering early in steps
 $t->get_ok('/steps')->status_is(200)->content_is('second');
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_PROXY}   = 0;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
@@ -49,4 +48,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();
@@ -4,8 +4,14 @@ use Test::More;
 use Mojo::ByteStream 'b';
 use Mojolicious::Routes::Pattern;
 
+# Text pattern (optimized)
+my $pattern = Mojolicious::Routes::Pattern->new('/test/123');
+is_deeply $pattern->match('/test/123'), {}, 'right structure';
+is_deeply $pattern->match('/test'), undef, 'no result';
+is $pattern->tree->[0][1], '/test/123', 'optimized pattern';
+
 # Normal pattern with text, placeholders and a default value
-my $pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action');
+$pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action');
 $pattern->defaults({action => 'index'});
 is_deeply $pattern->match('/test/foo/bar', 1),
   {controller => 'foo', action => 'bar'}, 'right structure';
@@ -55,10 +61,12 @@ 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';
-is $pattern->render, '/', 'right result';
+is $pattern->render, '', 'right result';
+is $pattern->render({format => 'txt'}, 1), '.txt', 'right result';
 
 # Regex in pattern
 $pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action/(id)',
@@ -246,12 +254,19 @@ is $pattern->render($result, 1), '/foo/bar', 'right result';
 $pattern = Mojolicious::Routes::Pattern->new('//');
 $result = $pattern->match('/', 1);
 is_deeply $result, {}, 'right structure';
-is $pattern->render($result, 1), '/', 'right result';
+is $pattern->render($result, 1), '', 'right result';
 $pattern = Mojolicious::Routes::Pattern->new('0');
 $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');
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -31,50 +28,57 @@ 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('');
 
 # Perldoc browser (Welcome)
-$t->get_ok('/perldoc')->status_is(200)->text_is('h1 a[id="NAME"]', 'NAME')
+$t->get_ok('/perldoc')->status_is(200)
   ->text_is('a[id="TUTORIAL"]', 'TUTORIAL')
-  ->text_is('a[id="GUIDES"]',   'GUIDES')->content_like(qr/galaxy/);
+  ->text_is('a[id="GUIDES"]',   'GUIDES')
+  ->content_like(qr/Mojolicious guide to the galaxy/);
 
 # Perldoc browser (Welcome with slash)
-$t->get_ok('/perldoc/')->status_is(200)->text_is('h1 a[id="NAME"]', 'NAME')
+$t->get_ok('/perldoc/')->status_is(200)
   ->text_is('a[id="TUTORIAL"]', 'TUTORIAL')
-  ->text_is('a[id="GUIDES"]',   'GUIDES')->content_like(qr/galaxy/)
-  ->content_unlike(qr/Gray/);
-
-# Perldoc browser (Mojolicious documentation)
-$t->get_ok('/perldoc/Mojolicious')->status_is(200)
-  ->text_is('h1 a[id="NAME"]', 'NAME')->text_is('a[id="handler"]', 'handler')
-  ->text_like('p', qr/Mojolicious/)->content_like(qr/Sebastian Riedel/);
-
-# Perldoc browser (Mojolicious documentation with format)
-$t->get_ok('/perldoc/Mojolicious.html')->status_is(200)
-  ->text_is('h1 a[id="NAME"]', 'NAME')->text_is('a[id="handler"]', 'handler')
-  ->text_like('p', qr/Mojolicious/)->content_like(qr/Sebastian Riedel/);
-
-# Perldoc browser (negotiated Mojolicious documentation)
-$t->get_ok('/perldoc/Mojolicious' => {Accept => 'text/html'})->status_is(200)
-  ->text_is('h1 a[id="NAME"]', 'NAME')->text_is('a[id="handler"]', 'handler')
-  ->text_like('p', qr/Mojolicious/)->content_like(qr/Sebastian Riedel/);
-
-# Perldoc browser (Mojolicious source with format)
-$t->get_ok('/perldoc/Mojolicious.txt')->status_is(200)
-  ->content_type_is('text/plain;charset=UTF-8')->content_like(qr/\$VERSION/);
+  ->text_is('a[id="GUIDES"]',   'GUIDES')
+  ->content_like(qr/Mojolicious guide to the galaxy/)
+  ->content_unlike(qr/Pirates/);
+
+# Perldoc browser (Mojo documentation)
+$t->get_ok('/perldoc/Mojo')->status_is(200)
+  ->text_is('h1 a[id="SYNOPSIS"]', 'SYNOPSIS')
+  ->text_is('a[id="handler"]',     'handler')
+  ->text_like('p', qr/Duct tape for the HTML5 web!/);
+
+# Perldoc browser (Mojo documentation with format)
+$t->get_ok('/perldoc/Mojo.html')->status_is(200)
+  ->text_is('h1 a[id="SYNOPSIS"]', 'SYNOPSIS')
+  ->text_is('a[id="handler"]',     'handler')
+  ->text_like('p', qr/Duct tape for the HTML5 web!/);
+
+# Perldoc browser (negotiated Mojo documentation)
+$t->get_ok('/perldoc/Mojo' => {Accept => 'text/html'})->status_is(200)
+  ->text_is('h1 a[id="SYNOPSIS"]', 'SYNOPSIS')
+  ->text_is('a[id="handler"]',     'handler')
+  ->text_like('p', qr/Duct tape for the HTML5 web!/);
+
+# Perldoc browser (Mojo source with format)
+$t->get_ok('/perldoc/Mojo.txt')->status_is(200)
+  ->content_type_is('text/plain;charset=UTF-8')
+  ->content_like(qr/package Mojo;/);
 
 # Perldoc browser (negotiated Mojolicious source again)
 $t->get_ok('/perldoc/Mojolicious' => {Accept => 'text/plain'})->status_is(200)
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'production';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -33,8 +32,13 @@ is_deeply $t->app->commands->namespaces,
 is $t->app, $t->app->commands->app, 'applications are equal';
 is $t->app->static->file('hello.txt')->slurp,
   "Hello Mojo from a static file!\n", 'right content';
+is $t->app->static->file('does_not_exist.html'), undef, 'no file';
 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,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::URL;
@@ -1,6 +1,7 @@
 use Mojo::Base -strict;
 
 use Test::More;
+use Mojo::Util 'decode';
 use Mojolicious::Controller;
 
 # Partial rendering
@@ -61,4 +62,60 @@ $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';
+
+# Reuse proxy objects
+my $helpers = $first->helpers;
+is $helpers->myapp->multi_level->test, $helpers->myapp->multi_level->test,
+  'same 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();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -38,6 +38,12 @@ $r->route('/alternatives4/:foo', foo => [qw(foo foo.bar)]);
 # /optional/*/*
 $r->route('/optional/:foo/:bar')->to(bar => 'test');
 
+# /optional2
+# /optional2/*
+# /optional2/*/*
+$r->route('/optional2/:foo')->to(foo => 'one')->route('/:bar')
+  ->to(bar => 'two');
+
 # /*/test
 my $test = $r->route('/:controller/test')->to(action => 'test');
 
@@ -180,11 +186,19 @@ $source->route('/second')->to('#second');
 my $third  = $source->route('/third')->to('#third');
 my $target = $r->remove->route('/target')->to('target#');
 my $second = $r->find('second');
-is $second->render('', {}), '/source/second', 'right result';
+is $second->render({}), '/source/second', 'right result';
 $second->remove;
-is $second->render('', {}), '/second', 'right result';
+is $second->render({}), '/second', 'right result';
 $target->add_child($first)->add_child($second);
-is $second->render('', {}), '/target/second', 'right result';
+is $second->render({}), '/target/second', 'right result';
+
+# /websocket
+$r->websocket('/websocket' => {controller => 'ws'})->route('/')
+  ->to(action => 'just')->route->to(works => 1);
+
+# /slash
+$r->route('/slash')->to(controller => 'just')->route('/')
+  ->to(action => 'slash');
 
 # /missing/*/name
 # /missing/too
@@ -194,6 +208,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';
@@ -355,6 +378,30 @@ is $m->path_for('optionalfoobar')->{path}, '/optional/23/24', 'right path';
 is $m->path_for('optionalfoobar', foo => 0)->{path}, '/optional/0/24',
   'right path';
 
+# Optional placeholders in nested routes
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2'});
+is_deeply $m->stack, [{foo => 'one', bar => 'two'}], 'right structure';
+is $m->path_for->{path}, '/optional2', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2.txt'});
+is_deeply $m->stack, [{foo => 'one', bar => 'two', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/optional2', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three'});
+is_deeply $m->stack, [{foo => 'three', bar => 'two'}], 'right structure';
+is $m->path_for->{path}, '/optional2/three', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three/four'});
+is_deeply $m->stack, [{foo => 'three', bar => 'four'}], 'right structure';
+is $m->path_for->{path}, '/optional2/three/four', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three/four.txt'});
+is_deeply $m->stack, [{foo => 'three', bar => 'four', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/optional2/three/four', 'right path';
+
 # Real world example using most features at once
 $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/articles/1/edit'});
@@ -815,6 +862,26 @@ $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/target/third'});
 is_deeply $m->stack, [], 'empty stack';
 
+# WebSocket
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/websocket'});
+is_deeply $m->stack, [], 'empty stack';
+$m->match($c => {method => 'GET', path => '/websocket', websocket => 1});
+is_deeply $m->stack, [{controller => 'ws', action => 'just', works => 1}],
+  'right structure';
+is $m->path_for->{path}, '/websocket', 'right path';
+ok $m->path_for->{websocket}, 'is a websocket';
+
+# Just a slash with a format after a path
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/slash.txt'});
+is_deeply $m->stack,
+  [{controller => 'just', action => 'slash', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/slash', 'right path';
+ok !$m->path_for->{websocket}, 'not a websocket';
+is $m->path_for(format => 'html')->{path}, '/slash.html', 'right path';
+
 # Nameless placeholder
 $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/missing/foo/name'});
@@ -848,4 +915,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();
@@ -1,25 +1,82 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
+use Mojo::Asset::Memory;
 use Mojo::Date;
 use Mojolicious::Lite;
 use Test::Mojo;
 
-get '/hello3.txt' => sub { shift->render_static('hello2.txt') };
+get '/hello3.txt' => sub { shift->reply->static('hello2.txt') };
+
+post '/hello4.txt' => sub {
+  my $c = shift;
+  $c->res->headers->content_type('text/html');
+  $c->reply->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!');
+};
+
+get '/asset' => sub {
+  my $c   = shift;
+  my $mem = Mojo::Asset::Memory->new->add_chunk('I <3 Assets!');
+  $c->reply->asset($mem);
+};
 
 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)')
@@ -57,7 +114,14 @@ $t->get_ok('/hello.txt' => {Range => 'bytes=0-0'})->status_is(206)
   ->header_is('Content-Range' => 'bytes 0-0/31')->content_is('H');
 
 # Partial static file, end outside of range
-$t->get_ok('/hello.txt' => {Range => 'bytes=25-35'})->status_is(206)
+$t->get_ok('/hello.txt' => {Range => 'bytes=25-31'})->status_is(206)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Content-Length' => 6)
+  ->header_is('Content-Range'  => 'bytes 25-30/31')
+  ->header_is('Accept-Ranges'  => 'bytes')->content_is("file!\n");
+
+# Partial static file, end way outside of range
+$t->get_ok('/hello.txt' => {Range => 'bytes=25-300'})->status_is(206)
   ->header_is(Server           => 'Mojolicious (Perl)')
   ->header_is('Content-Length' => 6)
   ->header_is('Content-Range'  => 'bytes 25-30/31')
@@ -80,6 +144,33 @@ $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"'})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->header_is(ETag => '"abc"')
+  ->content_is('');
+
+# Fresh asset
+$t->get_ok('/asset')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('I <3 Assets!');
+my $etag = $t->tx->res->headers->etag;
+
+# Stale asset
+$t->get_ok('/asset' => {'If-None-Match' => $etag})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Partial asset
+$t->get_ok('/asset' => {'Range' => 'bytes=3-5'})->status_is(206)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('3 A');
+
 # Empty file
 $t->get_ok('/hello4.txt')->status_is(200)
   ->header_is(Server           => 'Mojolicious (Perl)')
@@ -90,6 +181,9 @@ $t->get_ok('/hello4.txt' => {Range => 'bytes=0-0'})->status_is(416)
   ->header_is(Server          => 'Mojolicious (Perl)')
   ->header_is('Accept-Ranges' => 'bytes')->content_is('');
 
+# Hidden inline file
+$t->get_ok('/hidden')->status_is(404)->content_unlike(qr/Unreachable file/);
+
 # Base64 static inline file, If-Modified-Since
 my $modified = Mojo::Date->new->epoch(time - 3600);
 $t->get_ok('/static.txt' => {'If-Modified-Since' => $modified})
@@ -128,5 +222,8 @@ $t->get_ok('/static.txt' => {Range => 'bytes=45-50'})->status_is(416)
 done_testing();
 
 __DATA__
+@@ hidden
+Unreachable file.
+
 @@ static.txt (base64)
 dGVzdCAxMjMKbGFsYWxh
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -71,7 +68,8 @@ EOF
 
 # Shortcut
 $t->get_ok('/small_tags')->status_is(200)->content_is(<<EOF);
-<div>test &amp; 123</div>
+<div id="&amp;lt;">test &amp; 123</div>
+<div id="&lt;">test&nbsp;321</div>
 <div>
   <p id="0">just</p>
   <p>0</p>
@@ -497,7 +495,8 @@ __DATA__
 %= tag 'bar', class => 'test', ''
 
 @@ small_tags.html.ep
-%=t div => 'test & 123'
+%=t div => (id => '&lt;') => 'test & 123'
+%=t div => (id => b('&lt;')) => b('test&nbsp;321')
 %=t div => begin
   %=t p => (id => 0) => 'just'
   %=t p => 0
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -87,13 +84,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)
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::Asset::File;
@@ -26,7 +23,7 @@ post '/upload' => sub {
 
 post '/multi' => sub {
   my $c = shift;
-  my @uploads = map { $c->param($_) } $c->param('name');
+  my @uploads = map { @{$c->every_param($_)} } @{$c->every_param('name')};
   $c->render(text => join '', map { $_->filename, $_->asset->slurp } @uploads);
 };
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -36,9 +33,12 @@ my $t = Test::Mojo->new;
 # Required and optional values
 my $validation = $t->app->validation->input({foo => 'bar', baz => 'yada'});
 is_deeply [$validation->error], [], 'no names';
+is $validation->param('foo'), undef, 'no value';
+is_deeply $validation->every_param('foo'), [], 'no values';
 ok $validation->required('foo')->is_valid, 'valid';
 is_deeply $validation->output, {foo => 'bar'}, 'right result';
 is $validation->param('foo'), 'bar', 'right value';
+is_deeply $validation->every_param('foo'), ['bar'], 'right values';
 is_deeply [$validation->param], ['foo'], 'right names';
 ok !$validation->has_error, 'no error';
 ok $validation->optional('baz')->is_valid, 'valid';
@@ -77,6 +77,8 @@ is_deeply [$validation->error], [qw(baz yada)], 'right names';
 $validation = $t->app->validation->input(
   {foo => [qw(bar whatever)], baz => [qw(yada ohoh)]});
 ok $validation->required('foo')->in(qw(23 bar whatever))->is_valid, 'valid';
+is_deeply $validation->every_param('foo'), [qw(bar whatever)], 'right results';
+is $validation->param('foo'), 'whatever', 'right result';
 is_deeply $validation->output, {foo => [qw(bar whatever)]}, 'right result';
 ok !$validation->has_error, 'no error';
 ok !$validation->required('baz')->in(qw(yada whatever))->is_valid, 'not valid';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -132,12 +129,17 @@ $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)
   ->finished_ok(1000);
 
-# 64bit binary message (extended limit)
+# 64-bit binary message (extended limit)
 $t->request_ok($t->ua->build_websocket_tx('/echo'));
 is $t->tx->max_websocket_size, 262144, 'right size';
 $t->tx->max_websocket_size(262145);
@@ -145,11 +147,11 @@ $t->send_ok({binary => 'a' x 262145})
   ->message_ok->message_is({binary => 'a' x 262145})
   ->finish_ok->finished_ok(1005);
 
-# 64bit binary message (too large)
+# 64-bit binary message (too large)
 $t->websocket_ok('/echo')->send_ok({binary => 'b' x 262145})
   ->finished_ok(1009);
 
-# Binary message in two 64bit frames without FIN bit (too large)
+# Binary message in two 64-bit frames without FIN bit (too large)
 $t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'c' x 100000])
   ->send_ok([0, 0, 0, 0, 0, 'c' x 162146])->finished_ok(1009);
 
@@ -184,7 +186,7 @@ $t->message_ok->message_is({binary => 'a' x 50000});
 ok length $payload < 262145, 'message has been compressed';
 $t->finish_ok->finished_ok(1005);
 
-# Compressed message exceeding the limit when uncompressed
+# Compressed message exceeding the limit when decompressed
 $t->websocket_ok(
   '/echo' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
   ->header_is('Sec-WebSocket-Extensions' => 'permessage-deflate')
@@ -7,5 +7,7 @@ plan skip_all => 'set TEST_POD to enable this test (developer only!)'
 plan skip_all => 'Test::Pod::Coverage 1.04 required for this test!'
   unless eval 'use Test::Pod::Coverage 1.04; 1';
 
-# False positive constants
-all_pod_coverage_ok({also_private => [qw(IPV6 TLS)]});
+# DEPRECATED in Tiger Face!
+my @tiger = qw(decode encode error new pluck siblings val);
+
+all_pod_coverage_ok({also_private => [@tiger]});