<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/Mojo/Pipeline.pm</filename>
    </added>
    <added>
      <filename>t/mojolicious/templates/23.html.epl</filename>
    </added>
    <added>
      <filename>t/mojolicious/templates/layouts/default.html.epl</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -4,6 +4,8 @@ This file documents the revision history for Perl extension Mojo.
         - Rewrote MojoX::Renderer, it is not backward compatible and
           templates need to be renamed in the following 3 part format
           &quot;index.html.tt&quot;!
+        - Added full HTTP 1.1 pipelining support to all Mojo layers.
+        - Added layout support to MojoX::Renderer.
         - Made render call optional.
         - Added format support to MojoX::Routes.
         - Added wildcard symbol support to MojoX::Routes and rewrote many</diff>
      <filename>Changes</filename>
    </modified>
    <modified>
      <diff>@@ -24,6 +24,7 @@ WriteMakefile(
     PREREQ_PM =&gt; {
         'Carp'                  =&gt; 0,
         'Cwd'                   =&gt; 0,
+        'Data::Dumper'          =&gt; 0,
         'Digest::MD5'           =&gt; 0,
         'Encode'                =&gt; 0,
         'File::Basename'        =&gt; 0,</diff>
      <filename>Makefile.PL</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,6 @@ use base 'Mojo::Base';
 
 use IO::Socket::INET;
 use IO::Select;
-use Mojo;
 use Mojo::Loader;
 use Mojo::Message::Response;
 use Socket;
@@ -21,15 +20,7 @@ __PACKAGE__-&gt;attr(select_timeout     =&gt; (chained =&gt; 1, default =&gt; 5));
 sub connect {
     my ($self, $tx) = @_;
 
-    my $req  = $tx-&gt;req;
-    my $host = $req-&gt;url-&gt;host;
-    my $port = $req-&gt;url-&gt;port || 80;
-
-    # Proxy
-    if (my $proxy = $req-&gt;proxy) {
-        $host = $proxy-&gt;host;
-        $port = $proxy-&gt;port || 80;
-    }
+    my ($host, $port) = $tx-&gt;client_info;
 
     # Try to get a cached connection
     my $connection = $self-&gt;withdraw_connection(&quot;$host:$port&quot;);
@@ -47,26 +38,13 @@ sub connect {
 
         my $address = sockaddr_in($port, scalar inet_aton($host));
         $connection-&gt;connect($address);
-        $tx-&gt;{connect_timeout} = time + 5;
+        $tx-&gt;{_connect_timeout} = time + 5;
 
     }
     $tx-&gt;connection($connection);
-    $tx-&gt;state('connect');
-
-    # Connection header
-    unless ($req-&gt;headers-&gt;connection) {
-        if ($tx-&gt;keep_alive || $tx-&gt;kept_alive) {
-            $req-&gt;headers-&gt;connection('Keep-Alive');
-        }
-        else {
-            $req-&gt;headers-&gt;connection('Close');
-        }
-    }
 
-    # We identify ourself
-    my $version = $Mojo::VERSION;
-    $req-&gt;headers-&gt;user_agent(&quot;Mozilla/5.0 (compatible; Mojo/$version; Perl)&quot;)
-      unless $req-&gt;headers-&gt;user_agent;
+    # State machine
+    $tx-&gt;client_connect;
 
     return $tx;
 }
@@ -74,14 +52,11 @@ sub connect {
 sub disconnect {
     my ($self, $tx) = @_;
 
-    my $req  = $tx-&gt;req;
-    my $host = $req-&gt;url-&gt;host;
-    my $port = $req-&gt;url-&gt;port || 80;
-    my $peer = &quot;$host:$port&quot;;
+    my ($host, $port) = $tx-&gt;client_info;
 
     # Deposit connection for later or kill socket
     $tx-&gt;keep_alive
-      ? $self-&gt;deposit_connection($peer, $tx-&gt;connection)
+      ? $self-&gt;deposit_connection(&quot;$host:$port&quot;, $tx-&gt;connection)
       : $tx-&gt;connection(undef);
 
     return $tx;
@@ -167,30 +142,25 @@ sub spin {
     my $done = 0;
     for my $tx (@transactions) {
 
-        # Check for request/response errors
-        $tx-&gt;error('Request error.')  if $tx-&gt;req-&gt;has_error;
-        $tx-&gt;error('Response error.') if $tx-&gt;res-&gt;has_error;
-
         # Connect transaction
         $self-&gt;connect($tx) if $tx-&gt;is_state('start');
 
         # Check connect status
         if (!$tx-&gt;connection-&gt;connected) {
-            if (time &gt; $tx-&gt;{connect_timeout}) {
-                $tx-&gt;error(&quot;Can't connect to peer before timeout&quot;);
+            if (time &gt; $tx-&gt;{_connect_timeout}) {
+                $tx-&gt;error(&quot;Couldn't connect to peer before timeout.&quot;);
                 $done++;
             }
             next;
         }
+
+        # Connected
         elsif ($tx-&gt;is_state('connect')) {
 
-            # We might have to handle 100 Continue
-            $tx-&gt;{_continue} = $self-&gt;continue_timeout
-              if ($tx-&gt;req-&gt;headers-&gt;expect || '') =~ /100-continue/;
+            $tx-&gt;continue_timeout($self-&gt;continue_timeout);
 
-            # Ready for next state
-            $tx-&gt;state('write_start_line');
-            $tx-&gt;{_to_write} = $tx-&gt;req-&gt;start_line_length;
+            # State machine
+            $tx-&gt;client_connected;
 
             # Store connection information
             my ($lport, $laddr) = sockaddr_in(getsockname($tx-&gt;connection));
@@ -201,40 +171,13 @@ sub spin {
             $tx-&gt;remote_port($rport);
         }
 
+        # State machine
+        $tx-&gt;client_spin;
+
         # Map
         my $name = $self-&gt;_socket_name($tx-&gt;connection);
         $transaction{$name} = $tx;
 
-        # Request start line written
-        if ($tx-&gt;is_state('write_start_line')) {
-            if ($tx-&gt;{_to_write} &lt;= 0) {
-                $tx-&gt;state('write_headers');
-                $tx-&gt;{_offset}   = 0;
-                $tx-&gt;{_to_write} = $tx-&gt;req-&gt;header_length;
-            }
-        }
-
-        # Request headers written
-        if ($tx-&gt;is_state('write_headers')) {
-            if ($tx-&gt;{_to_write} &lt;= 0) {
-                $tx-&gt;{_continue}
-                  ? $tx-&gt;state('read_continue')
-                  : $tx-&gt;state('write_body');
-                $tx-&gt;{_offset}   = 0;
-                $tx-&gt;{_to_write} = $tx-&gt;req-&gt;body_length;
-            }
-        }
-
-        # 100 Continue timeout
-        if ($tx-&gt;is_state('read_continue')) {
-            $tx-&gt;state('write_body') unless $tx-&gt;{_continue};
-        }
-
-        # Request body written
-        if ($tx-&gt;is_state('write_body')) {
-            $tx-&gt;state('read_response') if $tx-&gt;{_to_write} &lt;= 0;
-        }
-
         # Done?
         if ($tx-&gt;is_done) {
             $done++;
@@ -278,16 +221,6 @@ sub spin {
       IO::Select-&gt;select($read_select, $write_select, undef,
         $self-&gt;select_timeout);
 
-    # Make sure we don't wait longer than 5 seconds for a 100 Continue
-    for my $tx (@transactions) {
-        next unless $tx-&gt;{_continue};
-        my $continue = $tx-&gt;{_continue};
-        $tx-&gt;{_started} ||= time;
-        $continue -= time - $tx-&gt;{_started};
-        $continue = 0 if $continue &lt; 0;
-        $tx-&gt;{_continue} = $continue;
-    }
-
     $read  ||= [];
     $write ||= [];
 
@@ -300,26 +233,15 @@ sub spin {
     # Write
     if ($do == 1) {
 
-        my ($tx, $req, $chunk);
+        my ($tx, $chunk);
 
-        # Check for content
+        # Check for content randomly
         for my $connection (sort { int(rand(3)) - 1 } @$write) {
 
             my $name = $self-&gt;_socket_name($connection);
-            $tx  = $transaction{$name};
-            $req = $tx-&gt;req;
-
-            # Body
-            $chunk = $req-&gt;get_body_chunk($tx-&gt;{_offset} || 0)
-              if $tx-&gt;is_state('write_body');
-
-            # Headers
-            $chunk = $req-&gt;get_header_chunk($tx-&gt;{_offset} || 0)
-              if $tx-&gt;is_state('write_headers');
+            $tx = $transaction{$name};
 
-            # Start line
-            $chunk = $req-&gt;get_start_line_chunk($tx-&gt;{_offset} || 0)
-              if $tx-&gt;is_state('write_start_line');
+            $chunk = $tx-&gt;client_get_chunk;
 
             # Content generator ready?
             last if defined $chunk;
@@ -327,11 +249,10 @@ sub spin {
 
         # Write chunk
         my $written = $tx-&gt;connection-&gt;syswrite($chunk, length $chunk);
-        $tx-&gt;error(&quot;Can't write request: $!&quot;) unless defined $written;
+        $tx-&gt;error(&quot;Can't write to socket: $!&quot;) unless defined $written;
         return 1 if $tx-&gt;has_error;
 
-        $tx-&gt;{_to_write} -= $written;
-        $tx-&gt;{_offset} += $written;
+        $tx-&gt;client_written($written);
     }
 
     # Read
@@ -340,43 +261,13 @@ sub spin {
         my $connection = $read-&gt;[rand(@$read)];
         my $name       = $self-&gt;_socket_name($connection);
         my $tx         = $transaction{$name};
-        my $res        = $tx-&gt;res;
-
-        # Early response, most likely an error
-        $tx-&gt;state('read_response')
-          if $tx-&gt;is_state(qw/write_start_line write_headers write_body/);
 
         my $buffer;
         my $read = $connection-&gt;sysread($buffer, 1024, 0);
         $tx-&gt;error(&quot;Can't read from socket: $!&quot;) unless defined $read;
         return 1 if $tx-&gt;has_error;
 
-        # Read 100 Continue
-        if ($tx-&gt;is_state('read_continue')) {
-            $res-&gt;done if $read == 0;
-            $res-&gt;parse($buffer);
-
-            # We got a 100 Continue response
-            if ($res-&gt;is_done &amp;&amp; $res-&gt;code == 100) {
-                $tx-&gt;res(Mojo::Message::Response-&gt;new);
-                $tx-&gt;continued(1);
-                $tx-&gt;{_continue} = 0;
-            }
-
-            # We got something else
-            elsif ($res-&gt;is_done) {
-                $tx-&gt;res($res);
-                $tx-&gt;continued(0);
-                $tx-&gt;done;
-            }
-        }
-
-        # Read response
-        elsif ($tx-&gt;is_state('read_response')) {
-            $tx-&gt;done if $read == 0;
-            $res-&gt;parse($buffer);
-            $tx-&gt;done if $res-&gt;is_done;
-        }
+        $tx-&gt;client_read($buffer);
     }
 
     return $done;</diff>
      <filename>lib/Mojo/Client.pm</filename>
    </modified>
    <modified>
      <diff>@@ -36,7 +36,8 @@ __PACKAGE__-&gt;attr(
         default =&gt; sub { Mojo::Headers-&gt;new }
     )
 );
-__PACKAGE__-&gt;attr(raw_header_length =&gt; (chained =&gt; 1, default =&gt; 0));
+__PACKAGE__-&gt;attr(
+    [qw/raw_header_length relaxed/] =&gt; (chained =&gt; 1, default =&gt; 0));
 
 sub build_body {
     my $self = shift;
@@ -125,6 +126,16 @@ sub is_multipart {
     return $type =~ /multipart/i ? 1 : 0;
 }
 
+sub leftovers {
+    my $self = shift;
+
+    # Chunked leftovers are in the filter buffer
+    return $self-&gt;filter_buffer-&gt;to_string if $self-&gt;is_chunked;
+
+    # Normal leftovers
+    return $self-&gt;buffer-&gt;to_string;
+}
+
 sub parse {
     my $self = shift;
 
@@ -177,15 +188,29 @@ sub parse {
         return Mojo::Content::MultiPart-&gt;new($self)-&gt;parse;
     }
 
-    # Parse body
-    $self-&gt;file-&gt;add_chunk($self-&gt;buffer-&gt;empty);
+    # Chunked body or relaxed content
+    if ($self-&gt;is_chunked || $self-&gt;relaxed) {
+        $self-&gt;file-&gt;add_chunk($self-&gt;buffer-&gt;empty);
+    }
+
+    # Normal body
+    else {
 
-    # Done
-    unless ($self-&gt;is_chunked) {
+        # Slurp
         my $length = $self-&gt;headers-&gt;content_length || 0;
+        my $need = $length - $self-&gt;file-&gt;length;
+        $self-&gt;file-&gt;add_chunk($self-&gt;buffer-&gt;remove($need)) if $need &gt; 0;
+
+        # Done
         $self-&gt;done if $length &lt;= $self-&gt;raw_body_length;
     }
 
+    # With leftovers, maybe pipelined
+    if ($self-&gt;is_done) {
+        $self-&gt;state('done_with_leftovers')
+          if $self-&gt;buffer-&gt;length || $self-&gt;filter_buffer-&gt;length;
+    }
+
     return $self;
 }
 
@@ -306,6 +331,11 @@ implements the following new ones.
 
     my $raw_body_length = $content-&gt;raw_body_length;
 
+=head2 C&lt;relaxed&gt;
+
+    my $relaxed = $content-&gt;relaxed;
+    $content    = $content-&gt;relaxed(1);
+
 =head1 METHODS
 
 L&lt;Mojo::Content&gt; inherits all methods from L&lt;Mojo::Stateful&gt; and implements
@@ -339,6 +369,10 @@ the following new ones.
 
     my $multipart = $content-&gt;is_multipart;
 
+=head2 C&lt;leftovers&gt;
+
+    my $bytes = $content-&gt;leftovers;
+
 =head2 C&lt;parse&gt;
 
     $content = $content-&gt;parse(&quot;Content-Length: 12\r\n\r\nHello World!&quot;);</diff>
      <filename>lib/Mojo/Content.pm</filename>
    </modified>
    <modified>
      <diff>@@ -206,14 +206,15 @@ sub _parse_multipart_boundary {
     # Begin
     if (index($self-&gt;buffer-&gt;{buffer}, &quot;\x0d\x0a--$boundary\x0d\x0a&quot;) == 0) {
         substr $self-&gt;buffer-&gt;{buffer}, 0, length($boundary) + 6, '';
-        push @{$self-&gt;parts}, Mojo::Content-&gt;new;
+        push @{$self-&gt;parts}, Mojo::Content-&gt;new(relaxed =&gt; 1);
         $self-&gt;state('multipart_body');
         return 1;
     }
 
     # End
-    if (index($self-&gt;buffer-&gt;{buffer}, &quot;\x0d\x0a--$boundary--&quot;) == 0) {
-        $self-&gt;buffer-&gt;empty;
+    my $end = &quot;\x0d\x0a--$boundary--&quot;;
+    if (index($self-&gt;buffer-&gt;{buffer}, $end) == 0) {
+        $self-&gt;buffer-&gt;{buffer} =~ s/^$end//;
         $self-&gt;done;
     }
 </diff>
      <filename>lib/Mojo/Content/MultiPart.pm</filename>
    </modified>
    <modified>
      <diff>@@ -75,7 +75,6 @@ sub parse {
             # Done
             else {
                 $self-&gt;_remove_chunked_encoding;
-                $filter-&gt;empty;
                 $self-&gt;done;
             }
             last;</diff>
      <filename>lib/Mojo/Filter/Chunked.pm</filename>
    </modified>
    <modified>
      <diff>@@ -270,6 +270,8 @@ sub is_chunked { shift-&gt;content-&gt;is_chunked }
 
 sub is_multipart { shift-&gt;content-&gt;is_multipart }
 
+sub leftovers { shift-&gt;content-&gt;leftovers }
+
 sub param {
     my $self = shift;
     $self-&gt;{body_params} ||= $self-&gt;body_params;
@@ -287,16 +289,27 @@ sub parse {
     $self-&gt;parser_progress_cb-&gt;($self) if $self-&gt;parser_progress_cb;
 
     # Content
-    if ($self-&gt;is_state(qw/content done/)) {
+    if ($self-&gt;is_state(qw/content done done_with_leftovers/)) {
         my $content = $self-&gt;content;
+
+        # HTTP 0.9 has no headers
         $content-&gt;state('body') if $self-&gt;version eq '0.9';
+
+        # Parse
         $content-&gt;filter_buffer($self-&gt;buffer);
         $self-&gt;content($content-&gt;parse);
+
+        # HTTP 0.9 has no defined length
+        $content-&gt;state('done') if $self-&gt;version eq '0.9';
     }
 
     # Done
     $self-&gt;done if $self-&gt;content-&gt;is_done;
 
+    # Done with leftovers, maybe pipelined
+    $self-&gt;state('done_with_leftovers')
+      if $self-&gt;content-&gt;is_state('done_with_leftovers');
+
     return $self;
 }
 
@@ -529,6 +542,13 @@ Defaults to 1.
 L&lt;Mojo::Message&gt; inherits all methods from L&lt;Mojo::Stateful&gt; and implements
 the following new ones.
 
+=head2 C&lt;at_least_version&gt;
+
+    my $success = $message-&gt;at_least_version('1.1');
+
+Returns true if the HTTP version is greater than or equal to the version
+passed in.
+
 =head2 C&lt;body&gt;
 
     my $string = $message-&gt;body;
@@ -608,12 +628,9 @@ followed HTTP version are set.
 
     my $is_multipart = $message-&gt;is_multipart;
 
-=head2 C&lt;at_least_version&gt;
+=head2 C&lt;leftovers&gt;
 
-    my $success = $message-&gt;at_least_version('1.1');
-
-Returns true if the HTTP version is greater than or equal to the version
-passed in.
+    my $bytes = $message-&gt;leftovers;
 
 =head2 C&lt;param&gt;
 </diff>
      <filename>lib/Mojo/Message.pm</filename>
    </modified>
    <modified>
      <diff>@@ -252,6 +252,10 @@ sub _parse_start_line {
                 $self-&gt;major_version(0);
                 $self-&gt;minor_version(9);
                 $self-&gt;done;
+
+                # HTTP 0.9 has no headers or body and does not support
+                # pipelining
+                $self-&gt;buffer-&gt;empty;
             }
         }
         else { $self-&gt;error('Parser error: Invalid request line') }</diff>
      <filename>lib/Mojo/Message/Request.pm</filename>
    </modified>
    <modified>
      <diff>@@ -158,6 +158,7 @@ sub _parse_start_line {
             $self-&gt;major_version(0);
             $self-&gt;minor_version(9);
             $self-&gt;state('content');
+            $self-&gt;content-&gt;relaxed(1);
             return 1;
         }
     }</diff>
      <filename>lib/Mojo/Message/Response.pm</filename>
    </modified>
    <modified>
      <diff>@@ -10,6 +10,7 @@ use base 'Mojo::Server';
 use Carp 'croak';
 use IO::Select;
 use IO::Socket;
+use Mojo::Pipeline;
 
 __PACKAGE__-&gt;attr(keep_alive_timeout =&gt; (chained =&gt; 1, default =&gt; 15));
 __PACKAGE__-&gt;attr(listen_queue_size  =&gt; (chained =&gt; 1, default =&gt; SOMAXCONN));
@@ -126,7 +127,7 @@ sub _prepare_select {
         my $connection = $self-&gt;{_connections}-&gt;{$name};
 
         # Transaction
-        my $tx = $connection-&gt;{tx};
+        my $p = $connection-&gt;{pipeline};
 
         # Keep alive timeout
         my $timeout = time - $connection-&gt;{time};
@@ -136,7 +137,7 @@ sub _prepare_select {
         }
 
         # No transaction in progress
-        unless ($tx) {
+        unless ($p) {
 
             # Keep alive request limit
             if ($connection-&gt;{requests} &gt;= $self-&gt;max_keep_alive_requests) {
@@ -149,12 +150,12 @@ sub _prepare_select {
             next;
         }
 
-        # Read
-        if ($tx-&gt;is_state('read')) { unshift @read, $tx-&gt;connection }
+        # We always try to read as sugegsted by the HTTP spec
+        unshift @read, $p-&gt;connection;
 
         # Write
-        if ($tx-&gt;is_state(qw/write_start_line write_headers write_body/)) {
-            unshift @write, $tx-&gt;connection;
+        if ($p-&gt;is_state(qw/write_start_line write_headers write_body/)) {
+            unshift @write, $p-&gt;connection;
         }
     }
 
@@ -179,58 +180,57 @@ sub _prepare_transactions {
         }
 
         # Transaction
-        my $tx = $connection-&gt;{tx};
+        my $p = $connection-&gt;{pipeline};
 
         # Just a keep alive, no transaction
-        next unless $tx;
-
-        # Writing
-        if ($tx-&gt;is_state('write')) {
-
-            # Connection header
-            unless ($tx-&gt;res-&gt;headers-&gt;connection) {
-                if ($tx-&gt;keep_alive) {
-                    $tx-&gt;res-&gt;headers-&gt;connection('Keep-Alive');
-                }
-                else {
-                    $tx-&gt;res-&gt;headers-&gt;connection('Close');
-                }
-            }
+        next unless $p;
 
-            # Ready for next state
-            $tx-&gt;state('write_start_line');
-            $tx-&gt;{_to_write} = $tx-&gt;res-&gt;start_line_length;
-        }
+        # Expect 100 Continue?
+        if ($p-&gt;is_state('handle_continue')) {
+
+            # Continue handler
+            $self-&gt;continue_handler_cb-&gt;($self, $p-&gt;server_tx);
 
-        # Response start line
-        if ($tx-&gt;is_state('write_start_line') &amp;&amp; $tx-&gt;{_to_write} &lt;= 0) {
-            $tx-&gt;state('write_headers');
-            $tx-&gt;{_offset}   = 0;
-            $tx-&gt;{_to_write} = $tx-&gt;res-&gt;header_length;
+            # Handled
+            $p-&gt;server_handled;
         }
 
-        # Response headers
-        if ($tx-&gt;is_state('write_headers') &amp;&amp; $tx-&gt;{_to_write} &lt;= 0) {
-            $tx-&gt;state('write_body');
-            $tx-&gt;{_offset}   = 0;
-            $tx-&gt;{_to_write} = $tx-&gt;res-&gt;body_length;
+        # EOF
+        if ($p-&gt;is_state('handle_request')) {
+
+            # Handler
+            $self-&gt;handler_cb-&gt;($self, $p-&gt;server_tx);
+
+            # Handled
+            $p-&gt;server_handled;
         }
 
-        # Response body
-        if ($tx-&gt;is_state('write_body') &amp;&amp; $tx-&gt;{_to_write} &lt;= 0) {
+        # State machine
+        $p-&gt;server_spin;
 
-            # Continue done
-            if (defined $tx-&gt;continued &amp;&amp; $tx-&gt;continued == 0) {
-                $tx-&gt;continued(1);
-                $tx-&gt;state('read');
-                $tx-&gt;done unless $tx-&gt;res-&gt;code == 100;
-                $tx-&gt;res-&gt;code(0);
-                next;
-            }
+        # Add transactions to the pipe for leftovers
+        if (my $leftovers = $p-&gt;server_leftovers) {
+
+            # New transaction
+            my $tx = $self-&gt;build_tx_cb-&gt;($self);
+            $tx-&gt;local_address($p-&gt;local_address);
+            $tx-&gt;local_port($p-&gt;local_port);
+            $tx-&gt;remote_address($p-&gt;remote_address);
+            $tx-&gt;remote_port($p-&gt;remote_port);
+
+            # Add to pipeline
+            $p-&gt;server_accept($tx);
+
+            # Read leftovers
+            $p-&gt;server_read($leftovers);
+        }
+
+        # Pipeline finished?
+        elsif ($p-&gt;is_finished) {
 
-            # Done
-            delete $connection-&gt;{tx};
-            $self-&gt;_drop_connection($name) unless $tx-&gt;keep_alive;
+            # Drop
+            delete $connection-&gt;{pipeline};
+            $self-&gt;_drop_connection($name) unless $p-&gt;keep_alive;
         }
     }
 }
@@ -256,28 +256,29 @@ sub _read {
     return 0 unless my $name = $self-&gt;_socket_name($socket);
 
     my $connection = $self-&gt;{_connections}-&gt;{$name};
-    unless ($connection-&gt;{tx}) {
-        my $tx = $connection-&gt;{tx} ||= $self-&gt;build_tx_cb-&gt;($self);
-        $tx-&gt;connection($socket);
-        $tx-&gt;state('read');
+    unless ($connection-&gt;{pipeline}) {
+
+        # New pipeline
+        my $p = $connection-&gt;{pipeline}
+          ||= Mojo::Pipeline-&gt;new-&gt;server_accept($self-&gt;build_tx_cb-&gt;($self));
+        $p-&gt;connection($socket);
         $connection-&gt;{requests}++;
-        $tx-&gt;kept_alive(1) if $connection-&gt;{requests} &gt; 1;
+        $p-&gt;kept_alive(1) if $connection-&gt;{requests} &gt; 1;
 
         # Last keep alive request?
-        $tx-&gt;res-&gt;headers-&gt;connection('Close')
+        $p-&gt;server_tx-&gt;res-&gt;headers-&gt;connection('Close')
           if $connection-&gt;{requests} &gt;= $self-&gt;max_keep_alive_requests;
 
         # Store connection information
-        my ($lport, $laddr) = sockaddr_in(getsockname($tx-&gt;connection));
-        $tx-&gt;local_address(inet_ntoa($laddr));
-        $tx-&gt;local_port($lport);
-        my ($rport, $raddr) = sockaddr_in(getpeername($tx-&gt;connection));
-        $tx-&gt;remote_address(inet_ntoa($raddr));
-        $tx-&gt;remote_port($rport);
+        my ($lport, $laddr) = sockaddr_in(getsockname($p-&gt;connection));
+        $p-&gt;local_address(inet_ntoa($laddr));
+        $p-&gt;local_port($lport);
+        my ($rport, $raddr) = sockaddr_in(getpeername($p-&gt;connection));
+        $p-&gt;remote_address(inet_ntoa($raddr));
+        $p-&gt;remote_port($rport);
     }
 
-    my $tx  = $connection-&gt;{tx};
-    my $req = $tx-&gt;req;
+    my $p = $connection-&gt;{pipeline};
 
     # Read request
     my $read = $socket-&gt;sysread(my $buffer, 4096, 0);
@@ -288,25 +289,8 @@ sub _read {
         return 1;
     }
 
-    # Parse
-    $req-&gt;parse($buffer);
-
-    # Expect 100 Continue?
-    if ($req-&gt;content-&gt;is_state('body') &amp;&amp; !defined $tx-&gt;continued) {
-        if (($req-&gt;headers-&gt;expect || '') =~ /100-continue/i) {
-            $tx-&gt;state('write');
-            $tx-&gt;continued(0);
-            $self-&gt;continue_handler_cb-&gt;($self, $tx);
-        }
-    }
-
-    # EOF
-    if (($read == 0) || $req-&gt;is_finished) {
-        $tx-&gt;state('write');
-
-        # Handle
-        $self-&gt;handler_cb-&gt;($self, $tx);
-    }
+    # Read
+    $p-&gt;server_read($buffer);
 
     $connection-&gt;{time} = time;
 }
@@ -330,26 +314,15 @@ sub _socket_name {
 sub _write {
     my ($self, $sockets) = @_;
 
-    my ($name, $tx, $res, $chunk);
+    my ($name, $p, $chunk);
 
     # Check for content
     for my $socket (@$sockets) {
         next unless $name = $self-&gt;_socket_name($socket);
         my $connection = $self-&gt;{_connections}-&gt;{$name};
-        $tx  = $connection-&gt;{tx};
-        $res = $tx-&gt;res;
-
-        # Body
-        $chunk = $res-&gt;get_body_chunk($tx-&gt;{_offset} || 0)
-          if $tx-&gt;is_state('write_body');
+        $p = $connection-&gt;{pipeline};
 
-        # Headers
-        $chunk = $res-&gt;get_header_chunk($tx-&gt;{_offset} || 0)
-          if $tx-&gt;is_state('write_headers');
-
-        # Start line
-        $chunk = $res-&gt;get_start_line_chunk($tx-&gt;{_offset} || 0)
-          if $tx-&gt;is_state('write_start_line');
+        $chunk = $p-&gt;server_get_chunk;
 
         # Content generator ready?
         last if defined $chunk;
@@ -357,13 +330,13 @@ sub _write {
     return 0 unless $name;
 
     # Write chunk
-    return 0 unless $tx-&gt;connection-&gt;connected;
-    my $written = $tx-&gt;connection-&gt;syswrite($chunk, length $chunk);
-    $tx-&gt;error(&quot;Can't write request: $!&quot;) unless defined $written;
-    return 1 if $tx-&gt;has_error;
+    return 0 unless $p-&gt;connection-&gt;connected;
+
+    my $written = $p-&gt;connection-&gt;syswrite($chunk, length $chunk);
+    $p-&gt;error(&quot;Can't write request: $!&quot;) unless defined $written;
+    return 1 if $p-&gt;has_error;
 
-    $tx-&gt;{_to_write} -= $written;
-    $tx-&gt;{_offset} += $written;
+    $p-&gt;server_written($written);
 
     $self-&gt;{_connections}-&gt;{$name}-&gt;{time} = time;
 }</diff>
      <filename>lib/Mojo/Server/Daemon.pm</filename>
    </modified>
    <modified>
      <diff>@@ -24,7 +24,7 @@ sub has_error { return defined shift-&gt;{error} }
 
 sub is_done { return shift-&gt;state eq 'done' }
 
-sub is_finished { return shift-&gt;is_state(qw/done error/) }
+sub is_finished { return shift-&gt;is_state(qw/done done_with_leftovers error/) }
 
 sub is_state {
     my ($self, @states) = @_;</diff>
      <filename>lib/Mojo/Stateful.pm</filename>
    </modified>
    <modified>
      <diff>@@ -7,13 +7,14 @@ use warnings;
 
 use base 'Mojo::Stateful';
 
+use Mojo;
 use Mojo::Message::Request;
 use Mojo::Message::Response;
 
 __PACKAGE__-&gt;attr(
     [   qw/
-          continued
           connection
+          continued
           kept_alive
           local_address
           local_port
@@ -22,6 +23,7 @@ __PACKAGE__-&gt;attr(
           /
     ] =&gt; (chained =&gt; 1)
 );
+__PACKAGE__-&gt;attr(continue_timeout =&gt; (chained =&gt; 1, default =&gt; 3));
 __PACKAGE__-&gt;attr(
     req =&gt; (
         chained =&gt; 1,
@@ -37,10 +39,200 @@ __PACKAGE__-&gt;attr(
 
 # What's a wedding?  Webster's dictionary describes it as the act of removing
 # weeds from one's garden.
+sub client_connect {
+    my $self = shift;
+
+    # Connect
+    $self-&gt;state('connect');
+
+    # Connection header
+    unless ($self-&gt;req-&gt;headers-&gt;connection) {
+        if ($self-&gt;keep_alive || $self-&gt;kept_alive) {
+            $self-&gt;req-&gt;headers-&gt;connection('Keep-Alive');
+        }
+        else {
+            $self-&gt;req-&gt;headers-&gt;connection('Close');
+        }
+    }
+
+    # We identify ourself
+    my $version = $Mojo::VERSION;
+    $self-&gt;req-&gt;headers-&gt;user_agent(
+        &quot;Mozilla/5.0 (compatible; Mojo/$version; Perl)&quot;)
+      unless $self-&gt;req-&gt;headers-&gt;user_agent;
+
+    return $self;
+}
+
+sub client_connected {
+    my $self = shift;
+
+    # We might have to handle 100 Continue
+    $self-&gt;{_continue} = $self-&gt;continue_timeout
+      if ($self-&gt;req-&gt;headers-&gt;expect || '') =~ /100-continue/;
+
+    # Ready for next state
+    $self-&gt;state('write_start_line');
+    $self-&gt;{_to_write} = $self-&gt;req-&gt;start_line_length;
+
+    return $self;
+}
+
+sub client_get_chunk {
+    my $self = shift;
+
+    my $chunk;
+
+    # Body
+    $chunk = $self-&gt;req-&gt;get_body_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_body');
+
+    # Headers
+    $chunk = $self-&gt;req-&gt;get_header_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_headers');
+
+    # Start line
+    $chunk = $self-&gt;req-&gt;get_start_line_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_start_line');
+
+    return $chunk;
+}
+
+sub client_info {
+    my $self = shift;
+
+    my $host = $self-&gt;req-&gt;url-&gt;host;
+    my $port = $self-&gt;req-&gt;url-&gt;port || 80;
+
+    # Proxy
+    if (my $proxy = $self-&gt;req-&gt;proxy) {
+        $host = $proxy-&gt;host;
+        $port = $proxy-&gt;port || 80;
+    }
+
+    return ($host, $port);
+}
+
+sub client_leftovers {
+    my $self = shift;
+
+    # No leftovers
+    return undef unless $self-&gt;is_state('done_with_leftovers');
+
+    # Leftovers
+    my $leftovers = $self-&gt;res-&gt;leftovers;
+    $self-&gt;done;
+
+    return $leftovers;
+}
+
+sub client_read {
+    my ($self, $chunk) = @_;
+
+    # Length
+    my $read = length $chunk;
+
+    # Early response, most likely an error
+    $self-&gt;state('read_response')
+      if $self-&gt;is_state(qw/write_start_line write_headers write_body/);
+
+    # Read 100 Continue
+    if ($self-&gt;is_state('read_continue')) {
+        $self-&gt;res-&gt;done if $read == 0;
+        $self-&gt;res-&gt;parse($chunk);
+
+        # We got a 100 Continue response
+        if ($self-&gt;res-&gt;is_done &amp;&amp; $self-&gt;res-&gt;code == 100) {
+            $self-&gt;res($self-&gt;res-&gt;new);
+            $self-&gt;continued(1);
+            $self-&gt;{_continue} = 0;
+        }
+
+        # We got something else
+        elsif ($self-&gt;res-&gt;is_done) {
+            $self-&gt;continued(0);
+            $self-&gt;done;
+        }
+    }
+
+    # Read response
+    elsif ($self-&gt;is_state('read_response')) {
+        $self-&gt;done if $read == 0;
+        $self-&gt;res-&gt;parse($chunk);
+        $self-&gt;done if $self-&gt;res-&gt;is_done;
+        $self-&gt;state('done_with_leftovers')
+          if $self-&gt;res-&gt;is_state('done_with_leftovers');
+    }
+
+    return $self;
+}
+
+sub client_spin {
+    my $self = shift;
+
+    # Check for request/response errors
+    $self-&gt;error('Request error.')  if $self-&gt;req-&gt;has_error;
+    $self-&gt;error('Response error.') if $self-&gt;res-&gt;has_error;
+
+    # Make sure we don't wait longer than 5 seconds for a 100 Continue
+    if ($self-&gt;{_continue}) {
+        my $continue = $self-&gt;{_continue};
+        $self-&gt;{_started} ||= time;
+        $continue -= time - $self-&gt;{_started};
+        $continue = 0 if $continue &lt; 0;
+        $self-&gt;{_continue} = $continue;
+    }
+
+    # Request start line written
+    if ($self-&gt;is_state('write_start_line')) {
+        if ($self-&gt;{_to_write} &lt;= 0) {
+            $self-&gt;state('write_headers');
+            $self-&gt;{_offset}   = 0;
+            $self-&gt;{_to_write} = $self-&gt;req-&gt;header_length;
+        }
+    }
+
+    # Request headers written
+    if ($self-&gt;is_state('write_headers')) {
+        if ($self-&gt;{_to_write} &lt;= 0) {
+            $self-&gt;{_continue}
+              ? $self-&gt;state('read_continue')
+              : $self-&gt;state('write_body');
+            $self-&gt;{_offset}   = 0;
+            $self-&gt;{_to_write} = $self-&gt;req-&gt;body_length;
+        }
+    }
+
+    # 100 Continue timeout
+    if ($self-&gt;is_state('read_continue')) {
+        $self-&gt;state('write_body') unless $self-&gt;{_continue};
+    }
+
+    # Request body written
+    if ($self-&gt;is_state('write_body')) {
+        $self-&gt;state('read_response') if $self-&gt;{_to_write} &lt;= 0;
+    }
+
+    return $self;
+}
+
+sub client_written {
+    my ($self, $written) = @_;
+
+    # Written
+    $self-&gt;{_to_write} -= $written;
+    $self-&gt;{_offset} += $written;
+
+    return $self;
+}
+
 sub keep_alive {
     my ($self, $keep_alive) = @_;
 
-    $self-&gt;{keep_alive} = $keep_alive if $keep_alive;
+    if ($keep_alive) {
+        $self-&gt;{keep_alive} = $keep_alive;
+        return $self;
+    }
 
     my $req = $self-&gt;req;
     my $res = $self-&gt;res;
@@ -74,6 +266,146 @@ sub new_head   { shift-&gt;_builder('HEAD',   @_) }
 sub new_post   { shift-&gt;_builder('POST',   @_) }
 sub new_put    { shift-&gt;_builder('PUT',    @_) }
 
+sub server_accept {
+    my $self = shift;
+
+    # Reading
+    $self-&gt;state('read');
+
+    return $self;
+}
+
+sub server_get_chunk {
+    my $self = shift;
+
+    my $chunk;
+
+    # Body
+    $chunk = $self-&gt;res-&gt;get_body_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_body');
+
+    # Headers
+    $chunk = $self-&gt;res-&gt;get_header_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_headers');
+
+    # Start line
+    $chunk = $self-&gt;res-&gt;get_start_line_chunk($self-&gt;{_offset} || 0)
+      if $self-&gt;is_state('write_start_line');
+
+    return $chunk;
+}
+
+sub server_handled {
+    my $self = shift;
+
+    # Handled and writing now
+    $self-&gt;state('write');
+
+    return $self;
+}
+
+sub server_read {
+    my ($self, $chunk) = @_;
+
+    # Parse
+    $self-&gt;req-&gt;parse($chunk);
+
+    # Expect 100 Continue?
+    if ($self-&gt;req-&gt;content-&gt;is_state('body') &amp;&amp; !defined $self-&gt;continued) {
+        if (($self-&gt;req-&gt;headers-&gt;expect || '') =~ /100-continue/i) {
+            $self-&gt;state('handle_continue');
+            $self-&gt;continued(0);
+        }
+    }
+
+    # EOF
+    if ((length $chunk == 0) || $self-&gt;req-&gt;is_finished) {
+        $self-&gt;state('handle_request');
+    }
+
+    return $self;
+}
+
+sub server_spin {
+    my $self = shift;
+
+    # Initialize
+    $self-&gt;{_to_write} ||= 0;
+
+    # Writing
+    if ($self-&gt;is_state('write')) {
+
+        # Connection header
+        unless ($self-&gt;res-&gt;headers-&gt;connection) {
+            if ($self-&gt;keep_alive) {
+                $self-&gt;res-&gt;headers-&gt;connection('Keep-Alive');
+            }
+            else {
+                $self-&gt;res-&gt;headers-&gt;connection('Close');
+            }
+        }
+
+        # Ready for next state
+        $self-&gt;state('write_start_line');
+        $self-&gt;{_to_write} = $self-&gt;res-&gt;start_line_length;
+    }
+
+    # Response start line
+    if ($self-&gt;is_state('write_start_line') &amp;&amp; $self-&gt;{_to_write} &lt;= 0) {
+        $self-&gt;state('write_headers');
+        $self-&gt;{_offset}   = 0;
+        $self-&gt;{_to_write} = $self-&gt;res-&gt;header_length;
+    }
+
+    # Response headers
+    if ($self-&gt;is_state('write_headers') &amp;&amp; $self-&gt;{_to_write} &lt;= 0) {
+        $self-&gt;state('write_body');
+        $self-&gt;{_offset}   = 0;
+        $self-&gt;{_to_write} = $self-&gt;res-&gt;body_length;
+    }
+
+    # Response body
+    if ($self-&gt;is_state('write_body') &amp;&amp; $self-&gt;{_to_write} &lt;= 0) {
+
+        # Continue done
+        if (defined $self-&gt;continued &amp;&amp; $self-&gt;continued == 0) {
+            $self-&gt;continued(1);
+            $self-&gt;state('read');
+
+            # Continue
+            if ($self-&gt;res-&gt;code == 100) {
+                $self-&gt;res($self-&gt;res-&gt;new);
+            }
+
+            # Don't continue
+            else { $self-&gt;done }
+        }
+
+        # Everything done
+        elsif (!defined $self-&gt;continued) {
+            $self-&gt;req-&gt;is_state('done_with_leftovers')
+              ? $self-&gt;state('done_with_leftovers')
+              : $self-&gt;state('done');
+        }
+    }
+
+    return $self;
+}
+
+sub server_written {
+    my ($self, $written) = @_;
+
+    # Written
+    $self-&gt;{_to_write} -= $written;
+    $self-&gt;{_offset} += $written;
+
+    # Done early
+    $self-&gt;state('done')
+      if $self-&gt;is_state('write_body') &amp;&amp; $self-&gt;{_to_write} &lt;= 0;
+
+    return $self;
+}
+
 sub _builder {
     my $class = shift;
     my $self  = $class-&gt;new;
@@ -126,6 +458,11 @@ implements the following new ones.
     my $connection = $tx-&gt;connection;
     $tx            = $tx-&gt;connection($connection);
 
+=head2 C&lt;continue_timeout&gt;
+
+    my $continue_timeout = $tx-&gt;continue_timeout;
+    $tx                  = $tx-&gt;continue_timeout(5);
+
 =head2 C&lt;continued&gt;
 
     my $continued = $tx-&gt;continued;
@@ -134,7 +471,7 @@ implements the following new ones.
 =head2 C&lt;keep_alive&gt;
 
     my $keep_alive = $tx-&gt;keep_alive;
-    my $keep_alive = $tx-&gt;keep_alive(1);
+    $tx            = $tx-&gt;keep_alive(1);
 
 =head2 C&lt;kept_alive&gt;
 
@@ -182,6 +519,38 @@ Returns the invocant if called with arguments.
 L&lt;Mojo::Transaction&gt; inherits all methods from L&lt;Mojo::Stateful&gt; and
 implements the following new ones.
 
+=head2 C&lt;client_connect&gt;
+
+    $tx = $tx-&gt;client_connect;
+
+=head2 C&lt;client_connected&gt;
+
+    $tx = $tx-&gt;client_connected;
+
+=head2 C&lt;client_get_chunk&gt;
+
+    my $chunk = $tx-&gt;client_get_chunk;
+
+=head2 C&lt;client_info&gt;
+
+    my ($host, $port) = $tx-&gt;client_info;
+
+=head2 C&lt;client_leftovers&gt;
+
+    my $leftovers = $tx-&gt;client_leftovers;
+
+=head2 C&lt;client_read&gt;
+
+    $tx = $tx-&gt;client_read($chunk);
+
+=head2 C&lt;client_spin&gt;
+
+    $tx = $tx-&gt;client_spin;
+
+=head2 C&lt;client_written&gt;
+
+    $tx = $tx-&gt;client_written($length);
+
 =head2 C&lt;new_delete&gt;
 
     my $tx = Mojo::Transaction-&gt;new_delete('http://127.0.0.1',
@@ -227,5 +596,28 @@ implements the following new ones.
         User-Agent =&gt; 'Mojo'
     });
 
+=head2 C&lt;server_accept&gt;
+
+    $tx = $tx-&gt;server_accept;
+
+=head2 C&lt;server_get_chunk&gt;
+
+    my $chunk = $tx-&gt;server_get_chunk;
+
+=head2 C&lt;server_handled&gt;
+
+    $tx = $tx-&gt;server_handled;
+
+=head2 C&lt;server_read&gt;
+
+    $tx = $tx-&gt;server_read($chunk);
+
+=head2 C&lt;server_spin&gt;
+
+    $tx = $tx-&gt;server_spin;
+
+=head2 C&lt;server_written&gt;
+
+    $tx = $tx-&gt;server_written($bytes);
 
 =cut</diff>
      <filename>lib/Mojo/Transaction.pm</filename>
    </modified>
    <modified>
      <diff>@@ -11,8 +11,6 @@ __PACKAGE__-&gt;attr(app =&gt; (chained =&gt; 1, weak =&gt; 1));
 __PACKAGE__-&gt;attr(tx =&gt; (chained =&gt; 1));
 
 # This is my first visit to the Galaxy of Terror and I'd like it to be a pleasant one.
-sub render { }
-
 sub req { return shift-&gt;tx-&gt;req }
 
 sub res { return shift-&gt;tx-&gt;res }
@@ -94,11 +92,6 @@ Returns the invocant if called with a hashref or multiple arguments.
 
 =head1 METHODS
 
-L&lt;MojoX::Context&gt; inherits all methods from L&lt;Mojo::Base&gt; and implements the
-following new ones.
-
-=head2 C&lt;render&gt;
-
-    $c-&gt;render;
+L&lt;MojoX::Context&gt; inherits all methods from L&lt;Mojo::Base&gt;.
 
 =cut</diff>
      <filename>lib/MojoX/Context.pm</filename>
    </modified>
    <modified>
      <diff>@@ -13,7 +13,7 @@ use Mojo::Loader;
 __PACKAGE__-&gt;attr(
     disallow =&gt; (
         chained =&gt; 1,
-        default =&gt; sub { [qw/new app attr render req res stash/] }
+        default =&gt; sub { [qw/new app attr meta render req res stash/] }
     )
 );
 __PACKAGE__-&gt;attr(namespace =&gt; (chained =&gt; 1));</diff>
      <filename>lib/MojoX/Dispatcher/Routes.pm</filename>
    </modified>
    <modified>
      <diff>@@ -11,6 +11,7 @@ __PACKAGE__-&gt;attr(match =&gt; (chained =&gt; 1));
 
 # Just make a simple cake. And this time, if someone's going to jump out of
 # it make sure to put them in *after* you cook it.
+sub render { }
 
 1;
 __END__
@@ -41,6 +42,10 @@ L&lt;MojoX::Context&gt; and implements the following new ones.
 =head1 METHODS
 
 L&lt;MojoX::Dispatcher::Routes::Context&gt; inherits all methods from
-L&lt;MojoX::Context&gt;.
+L&lt;MojoX::Context&gt; and implements the following new ones.
+
+=head2 C&lt;render&gt;
+
+    $c-&gt;render;
 
 =cut</diff>
      <filename>lib/MojoX/Dispatcher/Routes/Context.pm</filename>
    </modified>
    <modified>
      <diff>@@ -36,67 +36,49 @@ sub add_handler {
 sub render {
     my ($self, $c) = @_;
 
-    my $format        = $c-&gt;stash-&gt;{format};
-    my $handler       = $c-&gt;stash-&gt;{handler};
-    my $template      = $c-&gt;stash-&gt;{template};
-    my $template_path = $c-&gt;stash-&gt;{template_path};
+    my ($template, $template_path);
+    my $is_layout = 0;
+
+    # Layout first
+    if ($c-&gt;stash-&gt;{layout} || $c-&gt;stash-&gt;{layout_path}) {
+        $template      = $c-&gt;stash-&gt;{layout};
+        $template_path = $c-&gt;stash-&gt;{layout_path};
+        $is_layout     = 1;
+    }
+
+    # Normal template
+    else {
+        $template      = $c-&gt;stash-&gt;{template};
+        $template_path = $c-&gt;stash-&gt;{template_path};
+    }
 
-    # Not enough informations
+    # Not enough information
     return undef unless $template || $template_path;
 
     # Handler precedence
     $self-&gt;precedence([sort keys %{$self-&gt;handler}])
       unless $self-&gt;precedence;
 
-    # Template path
-    $template_path = File::Spec-&gt;catfile($self-&gt;root, $template)
-      if $template &amp;&amp; !$template_path;
-
-    # Format
-    unless ($template_path =~ /\.\w+(?:\.\w+)?$/) {
-        if ($format) { $template_path .= &quot;.$format&quot; }
-        else {
-            $c-&gt;app-&gt;log-&gt;debug('Template format missing');
-            return undef;
-        }
-    }
+    # Inner
+    local $c-&gt;stash-&gt;{inner_template} = $c-&gt;stash-&gt;{template} if $is_layout;
+    local $c-&gt;stash-&gt;{inner_template_path} = $c-&gt;stash-&gt;{template_path}
+      if $is_layout;
 
-    # Handler
-    unless ($template_path =~ /\.\w+\.\w+$/) {
-
-        if ($handler) { $template_path .= &quot;.$handler&quot; }
-
-        # Detect
-        else {
-            my $found = 0;
-            for my $ext (@{$self-&gt;precedence}) {
-
-                # Try
-                my $path = &quot;$template_path.$ext&quot;;
-                if (-f $path) {
-                    $found++;
-                    $template_path = $path;
-                    $c-&gt;app-&gt;log-&gt;debug(qq/Template found &quot;$template_path&quot;/);
-                    last;
-                }
-            }
+    # Template has priority
+    $template_path = undef if $template;
 
-            # Nothing found
-            unless ($found) {
-                $c-&gt;app-&gt;log-&gt;debug(
-                    qq/Template not found &quot;$template_path.*&quot;/);
-                return undef;
-            }
-        }
-    }
+    # Path
+    return undef
+      unless $template_path =
+          $self-&gt;_fix_path($c, $template, $template_path, $is_layout);
 
     # Store for handler usage
     local $c-&gt;stash-&gt;{template_path} = $template_path;
 
     # Extract
     $template_path =~ /\.(\w+)\.(\w+)$/;
-    $format  = $1;
-    $handler = $2;
+    my $format  = $1;
+    my $handler = $2;
 
     # Renderer
     my $r = $self-&gt;handler-&gt;{$handler};
@@ -110,6 +92,11 @@ sub render {
     # Partial?
     my $partial = $c-&gt;stash-&gt;{partial};
 
+    # Clean
+    local $c-&gt;stash-&gt;{layout}      = undef;
+    local $c-&gt;stash-&gt;{layout_path} = undef;
+    local $c-&gt;stash-&gt;{template}    = undef;
+
     # Render
     my $output;
     return undef unless $r-&gt;($self, $c, \$output);
@@ -132,6 +119,84 @@ sub render {
     return 1;
 }
 
+sub _fix_format {
+    my ($self, $c, $path) = @_;
+
+    # Format ok
+    return $path if $path =~ /\.\w+(?:\.\w+)?$/;
+
+    my $format = $c-&gt;stash-&gt;{format};
+
+    # Append format
+    if ($format) { $path .= &quot;.$format&quot; }
+
+    # Missing format
+    else {
+        $c-&gt;app-&gt;log-&gt;debug('Template format missing');
+        return undef;
+    }
+
+    return $path;
+}
+
+sub _fix_handler {
+    my ($self, $c, $path) = @_;
+
+    # Handler ok
+    return $path if $path =~ /\.\w+\.\w+$/;
+
+    my $handler = $c-&gt;stash-&gt;{handler};
+
+    # Append handler
+    if ($handler) { $path .= &quot;.$handler&quot; }
+
+    # Detect
+    else {
+        my $found = 0;
+        for my $ext (@{$self-&gt;precedence}) {
+
+            # Try
+            my $p = &quot;$path.$ext&quot;;
+            if (-f $p) {
+                $found++;
+                $path = $p;
+                $c-&gt;app-&gt;log-&gt;debug(qq/Template found &quot;$path&quot;/);
+                last;
+            }
+        }
+
+        # Nothing found
+        unless ($found) {
+            $c-&gt;app-&gt;log-&gt;debug(qq/Template not found &quot;$path.*&quot;/);
+            return undef;
+        }
+    }
+
+    return $path;
+}
+
+sub _fix_path {
+    my ($self, $c, $template, $template_path, $is_layout) = @_;
+
+    # Root
+    my $root =
+      $is_layout ? File::Spec-&gt;catfile($self-&gt;root, 'layouts') : $self-&gt;root;
+
+    # Path
+    $template_path = File::Spec-&gt;catfile($root, $template)
+      if $template &amp;&amp; !$template_path;
+
+    # Format
+    return undef
+      unless $template_path = $self-&gt;_fix_format($c, $template_path);
+
+    # Handler
+    return undef
+      unless $template_path = $self-&gt;_fix_handler($c, $template_path);
+
+    return $template_path;
+}
+
 1;
 __END__
 </diff>
      <filename>lib/MojoX/Renderer.pm</filename>
    </modified>
    <modified>
      <diff>@@ -19,7 +19,7 @@ sub render {
     $self-&gt;{stash} = {%{$self-&gt;stash}, %$args};
 
     # Template
-    unless ($self-&gt;stash-&gt;{template}) {
+    unless ($self-&gt;stash-&gt;{template} || $self-&gt;stash-&gt;{template_path}) {
 
         # Default template
         my $controller = $self-&gt;stash-&gt;{controller};
@@ -38,6 +38,19 @@ sub render {
     return $self-&gt;app-&gt;renderer-&gt;render($self);
 }
 
+sub render_inner {
+    my $self = shift;
+    local $self-&gt;stash-&gt;{template}      = $self-&gt;stash-&gt;{inner_template};
+    local $self-&gt;stash-&gt;{template_path} = $self-&gt;stash-&gt;{inner_template_path};
+    return $self-&gt;render_partial(@_);
+}
+
+sub render_partial {
+    my $self = shift;
+    local $self-&gt;stash-&gt;{partial} = 1;
+    return $self-&gt;render(@_);
+}
+
 sub url_for {
     my $self = shift;
 
@@ -90,6 +103,16 @@ L&lt;MojoX::Dispatcher::Routes::Context&gt; and implements the following new ones.
     $c-&gt;render;
     $c-&gt;render(action =&gt; 'foo');
 
+=head2 C&lt;render_inner&gt;
+
+    $c-&gt;render_inner;
+    $c-&gt;render_inner(action =&gt; 'foo');
+
+=head2 C&lt;render_partial&gt;
+
+    $c-&gt;render_partial;
+    $c-&gt;render_partial(action =&gt; 'foo');
+
 =head2 C&lt;url_for&gt;
 
     my $url = $c-&gt;url_for;</diff>
      <filename>lib/Mojolicious/Context.pm</filename>
    </modified>
    <modified>
      <diff>@@ -48,10 +48,12 @@ sub run {
     $self-&gt;render_to_rel_file('404',    &quot;$name/public/404.html&quot;);
     $self-&gt;render_to_rel_file('static', &quot;$name/public/index.html&quot;);
 
-    # Template
+    # Layout and Template
     $self-&gt;renderer-&gt;line_start('%%');
     $self-&gt;renderer-&gt;tag_start('&lt;%%');
     $self-&gt;renderer-&gt;tag_end('%%&gt;');
+    $self-&gt;render_to_rel_file('layout',
+        &quot;$name/templates/layouts/default.html.epl&quot;);
     $self-&gt;render_to_rel_file('welcome',
         &quot;$name/templates/example/welcome.html.epl&quot;);
 }
@@ -180,8 +182,11 @@ use base 'Mojolicious::Controller';
 sub welcome {
     my $self = shift;
 
-    # Render template &quot;example/welcome.html.epl&quot; with welcome message
-    $self-&gt;render(message =&gt; 'Welcome to the Mojolicious Web Framework!');
+    # Render template &quot;example/welcome.html.epl&quot; with message and layout
+    $self-&gt;render(
+        layout  =&gt; 'default',
+        message =&gt; 'Welcome to the Mojolicious Web Framework!'
+    );
 }
 
 1;
@@ -229,21 +234,21 @@ $client-&gt;process_local('&lt;%= $class %&gt;', $tx);
 is($tx-&gt;res-&gt;code, 200);
 is($tx-&gt;res-&gt;headers-&gt;content_type, 'text/html');
 like($tx-&gt;res-&gt;content-&gt;file-&gt;slurp, qr/Mojolicious Web Framework/i);
-__welcome__
+__layout__
 % my $self = shift;
 &lt;!doctype html&gt;
-    &lt;head&gt;&lt;title&gt;&lt;%= $self-&gt;stash('message') %&gt;&lt;/title&gt;&lt;/head&gt;
+    &lt;head&gt;&lt;title&gt;Welcome&lt;/title&gt;&lt;/head&gt;
     &lt;body&gt;
-        &lt;h2&gt;&lt;%= $self-&gt;stash('message') %&gt;&lt;/h2&gt;
-        This page was generated from the template
-        &quot;templates/example/welcome.html.epl&quot;,
-        &lt;a href=&quot;&lt;%= $self-&gt;url_for %&gt;&quot;&gt;
-            click here
-        &lt;/a&gt; 
-        to reload the page or
-        &lt;a href=&quot;/index.html&quot;&gt;
-            here
-        &lt;/a&gt;
-        to move forward to a static page.
+        &lt;%= $self-&gt;render_inner %&gt;
     &lt;/body&gt;
 &lt;/html&gt;
+__welcome__
+% my $self = shift;
+&lt;h2&gt;&lt;%= $self-&gt;stash('message') %&gt;&lt;/h2&gt;
+This page was generated from the template
+&quot;templates/example/welcome.html.epl&quot; and the layout
+&quot;templates/layouts/default.html.epl&quot;,
+&lt;a href=&quot;&lt;%= $self-&gt;url_for %&gt;&quot;&gt;click here&lt;/a&gt;
+to reload the page or
+&lt;a href=&quot;/index.html&quot;&gt;here&lt;/a&gt;
+to move forward to a static page.</diff>
      <filename>lib/Mojolicious/Script/Generate/App.pm</filename>
    </modified>
    <modified>
      <diff>@@ -9,11 +9,12 @@ use Test::More;
 
 plan skip_all =&gt; 'set TEST_CLIENT to enable this test'
   unless $ENV{TEST_CLIENT};
-plan tests =&gt; 16;
+plan tests =&gt; 34;
 
 # So then I said to the cop, &quot;No, you're driving under the influence...
 # of being a jerk&quot;.
 use_ok('Mojo::Client');
+use_ok('Mojo::Pipeline');
 use_ok('Mojo::Transaction');
 
 # Parallel async io
@@ -62,3 +63,35 @@ ok($tx-&gt;local_address);
 ok($tx-&gt;local_port &gt; 0);
 is($tx-&gt;remote_address, '88.198.25.164');
 is($tx-&gt;remote_port,    80);
+
+# Pipelined
+$tx  = Mojo::Transaction-&gt;new_get('http://labs.kraih.com');
+$tx2 = Mojo::Transaction-&gt;new_get('http://mojolicious.org');
+my $tx3 = Mojo::Transaction-&gt;new_get('http://kraih.com');
+$client-&gt;process_all(Mojo::Pipeline-&gt;new($tx, $tx2), $tx3);
+ok($tx-&gt;is_done);
+ok($tx2-&gt;is_done);
+ok($tx3-&gt;is_done);
+is($tx-&gt;res-&gt;code,  301);
+is($tx2-&gt;res-&gt;code, 200);
+is($tx3-&gt;res-&gt;code, 200);
+like($tx2-&gt;res-&gt;content-&gt;file-&gt;slurp, qr/Mojolicious/);
+
+# Pipelined with 100 Continue
+$tx  = Mojo::Transaction-&gt;new_get('http://labs.kraih.com');
+$tx2 = Mojo::Transaction-&gt;new_get('http://mojolicious.org');
+$tx2-&gt;req-&gt;headers-&gt;expect('100-continue');
+$tx2-&gt;req-&gt;body('foo bar baz');
+$tx3 = Mojo::Transaction-&gt;new_get('http://labs.kraih.com/blog/');
+my $tx4 = Mojo::Transaction-&gt;new_get('http://labs.kraih.com/blog');
+$client-&gt;process_all(Mojo::Pipeline-&gt;new($tx, $tx2, $tx3, $tx4));
+ok($tx-&gt;is_done);
+ok($tx2-&gt;is_done);
+ok($tx3-&gt;is_done);
+ok($tx4-&gt;is_done);
+is($tx-&gt;res-&gt;code,  301);
+is($tx2-&gt;res-&gt;code, 200);
+is($tx2-&gt;continued, 1);
+is($tx3-&gt;res-&gt;code, 200);
+is($tx4-&gt;res-&gt;code, 301);
+like($tx2-&gt;res-&gt;content-&gt;file-&gt;slurp, qr/Mojolicious/);</diff>
      <filename>t/mojo/client.t</filename>
    </modified>
    <modified>
      <diff>@@ -8,12 +8,13 @@ use warnings;
 use Test::More;
 
 use Mojo::Client;
+use Mojo::Pipeline;
 use Mojo::Transaction;
 use Test::Mojo::Server;
 
 plan skip_all =&gt; 'set TEST_DAEMON to enable this test (developer only!)'
   unless $ENV{TEST_DAEMON};
-plan tests =&gt; 16;
+plan tests =&gt; 31;
 
 # Daddy, I'm scared. Too scared to even wet my pants.
 # Just relax and it'll come, son.
@@ -61,5 +62,34 @@ is($tx-&gt;kept_alive, 1);
 like($tx-&gt;res-&gt;headers-&gt;connection, qr/Keep-Alive/i);
 like($tx-&gt;res-&gt;body,                qr/Mojo is working/);
 
+# Pipelined
+$tx = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+my $tx2 = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+$client-&gt;process_all(Mojo::Pipeline-&gt;new($tx, $tx2));
+ok($tx-&gt;is_done);
+ok($tx2-&gt;is_done);
+is($tx-&gt;res-&gt;code,  200);
+is($tx2-&gt;res-&gt;code, 200);
+like($tx2-&gt;res-&gt;content-&gt;file-&gt;slurp, qr/Mojo is working/);
+
+# Pipelined with 100 Continue
+$tx  = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+$tx2 = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+$tx2-&gt;req-&gt;headers-&gt;expect('100-continue');
+$tx2-&gt;req-&gt;body('foo bar baz');
+my $tx3 = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+my $tx4 = Mojo::Transaction-&gt;new_get(&quot;http://127.0.0.1:$port/&quot;);
+$client-&gt;process_all(Mojo::Pipeline-&gt;new($tx, $tx2, $tx3, $tx4));
+ok($tx-&gt;is_done);
+ok($tx2-&gt;is_done);
+ok($tx3-&gt;is_done);
+ok($tx4-&gt;is_done);
+is($tx-&gt;res-&gt;code,  200);
+is($tx2-&gt;res-&gt;code, 200);
+is($tx2-&gt;continued, 1);
+is($tx3-&gt;res-&gt;code, 200);
+is($tx4-&gt;res-&gt;code, 200);
+like($tx2-&gt;res-&gt;content-&gt;file-&gt;slurp, qr/Mojo is working/);
+
 # Stop
 $server-&gt;stop_server_ok;</diff>
      <filename>t/mojo/daemon.t</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@
 use strict;
 use warnings;
 
-use Test::More tests =&gt; 234;
+use Test::More tests =&gt; 240;
 
 use Mojo::Filter::Chunked;
 use Mojo::Headers;
@@ -32,6 +32,16 @@ is($req-&gt;major_version, 1);
 is($req-&gt;minor_version, 1);
 is($req-&gt;url,           '/');
 
+# Parse pipelined HTTP 1.1 start line, no headers and body
+$req = Mojo::Message::Request-&gt;new;
+$req-&gt;parse(&quot;GET / HTTP/1.1\x0d\x0a\x0d\x0aGET / HTTP/1.1\x0d\x0a\x0d\x0a&quot;);
+is($req-&gt;state,         'done_with_leftovers');
+is($req-&gt;method,        'GET');
+is($req-&gt;major_version, 1);
+is($req-&gt;minor_version, 1);
+is($req-&gt;url,           '/');
+is($req-&gt;leftovers,     &quot;GET / HTTP/1.1\x0d\x0a\x0d\x0a&quot;);
+
 # Parse HTTP 1.0 start line and headers, no body
 $req = Mojo::Message::Request-&gt;new;
 $req-&gt;parse(&quot;GET /foo/bar/baz.html HTTP/1.0\x0d\x0a&quot;);</diff>
      <filename>t/mojo/message.t</filename>
    </modified>
    <modified>
      <diff>@@ -93,7 +93,7 @@ is($url-&gt;to_abs,         'http://kraih.com/bar/baz/../foo?foo=bar#23');
 is($url-&gt;to_abs-&gt;to_rel, '../foo?foo=bar#23');
 is($url-&gt;to_abs-&gt;base,   'http://kraih.com/bar/baz/');
 
-# Real world test
+# Real world tests
 $url = Mojo::URL-&gt;new('http://acme.s3.amazonaws.com'
       . '/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb');
 is($url-&gt;is_abs,   1);</diff>
      <filename>t/mojo/url.t</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,7 @@ $tx = Mojo::Transaction-&gt;new_get('/foo', 'X-Test' =&gt; 'Hi there!');
 $client-&gt;process_local('MojoliciousTest', $tx);
 is($tx-&gt;res-&gt;code,                  200);
 is($tx-&gt;res-&gt;headers-&gt;content_type, 'text/html');
-like($tx-&gt;res-&gt;body, qr/Hello Mojo from the template \/foo! Hello World!/);
+like($tx-&gt;res-&gt;body, qr/&lt;body&gt;\n23Hello Mojo from the template \/foo! He/);
 
 # Foo::Bar::index
 $tx = Mojo::Transaction-&gt;new_get('/foo-bar', 'X-Test' =&gt; 'Hi there!');</diff>
      <filename>t/mojolicious/app.t</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,7 @@ use base 'Mojolicious::Controller';
 # Let me check my program... Yep.
 sub index {
     my $self = shift;
-    $self-&gt;stash(msg =&gt; 'Hello World!');
+    $self-&gt;stash(layout =&gt; 'default', msg =&gt; 'Hello World!');
 }
 
 sub templateless {</diff>
      <filename>t/mojolicious/lib/MojoliciousTest/Foo.pm</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>685a370c882b1e7f22fde88f00eb222c14cbb2c2</id>
    </parent>
  </parents>
  <author>
    <name>Sebastian Riedel</name>
    <login>kraih</login>
    <email>sri@labs.kraih.com</email>
  </author>
  <url>http://github.com/kraih/mojo/commit/3114e47ddcbe132a1f0298572caf18231c8313fc</url>
  <id>3114e47ddcbe132a1f0298572caf18231c8313fc</id>
  <committed-date>2009-04-18T17:15:24-07:00</committed-date>
  <authored-date>2009-04-18T17:15:24-07:00</authored-date>
  <message>Added HTTP 1.1 pipelining support to Mojo and layouts to Mojolicious</message>
  <tree>1c823870140e6ef20b820ac8ecf6b5a81bcc4a1a</tree>
  <committer>
    <name>Sebastian Riedel</name>
    <login>kraih</login>
    <email>sri@labs.kraih.com</email>
  </committer>
</commit>
