Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sign In with Google Sign In with OpenID

Chunked Transfer Encoding need some precisions

edited May 2013 in Documentation

Hello !

I've had a hard time testing the Controller::flush() method as explained in the documentation : http://www.elefantcms.com/wiki/Chunked-transfer-encoding

What i've discovered so far:

  • this doesn't works at all with apache mod_deflate module enabled
  • if you call $this->flush() anywhere in a handler, you need to call $this->flush(null) at the end of the handler, or the whole thing is misunderstood by the browser (chunk sizes are displayed in the page)

Steps to reproduce errors:

$ ./elefant build-app demo

vim apps/demo/handlers/stream.php (copy/paste from the documentation example)

<?php

$list = array ('Item one', 'Item two', 'Item three', 'Item four');

echo '<ul>';
foreach ($list as $item) {
    printf ('<li>%s</li>', $item);
    $this->flush ();
}
echo '</ul>';

?>

visit /demo/stream, you'll obtain this:

15
Item one
11
Item two
13
Item three
12
Item four
5

add a $this->flush(null); just after the last echo, and it works fine (as long as apache mod_deflate is disabled)

As a note side, apache mod_deflate can be disabled in a vhost by using SetEnv no-gzip 1 in the vhost config file.

Hope this help someone wanting to send some chunked content with apache :-)

Comments

  • It seems like adding conf ('General', 'compress_output', false); to disable gzip compression in Elefant also works. For me, I don't seem to need the $this->flush(null); after that though.

  • Here's what I have:

    $ cat apps/demo/handlers/stream0.php 
    <?php
    $list = array ('Item one', 'Item two', 'Item three', 'Item four');
    echo '<ul>';
    foreach ($list as $item) {
        printf ('<li>%s</li>', $item);
        $this->flush ();
    }
    echo "</ul>\n";
    ?>
    
    $ curl -i http://elefant.local/demo/stream0
    HTTP/1.1 200 OK
    Date: Tue, 21 May 2013 16:46:46 GMT
    Server: Apache/2.2.22 (Ubuntu)
    X-Powered-By: PHP/5.4.15-1~precise+1
    Transfer-Encoding: chunked
    Vary: Accept-Encoding
    Content-Type: text/html
    
    15
    <ul><li>Item one</li>
    11
    <li>Item two</li>
    13
    <li>Item three</li>
    12
    <li>Item four</li>
    6
    </ul>
    

    The Content-Encoding: chunked is not decoded, as there is no final chunk of zero length. But this works:

    $ cat apps/demo/handlers/stream1.php 
    <?php
    $list = array ('Item one', 'Item two', 'Item three', 'Item four');
    echo '<ul>';
    foreach ($list as $item) {
        printf ('<li>%s</li>', $item);
        $this->flush ();
    }
    echo "</ul>\n";
    $this->flush(null);
    ?>
    
    $ curl -i http://elefant.local/demo/stream1
    HTTP/1.1 200 OK
    Date: Tue, 21 May 2013 16:50:08 GMT
    Server: Apache/2.2.22 (Ubuntu)
    X-Powered-By: PHP/5.4.15-1~precise+1
    Transfer-Encoding: chunked
    Vary: Accept-Encoding
    Content-Type: text/html
    
    <ul><li>Item one</li><li>Item two</li><li>Item three</li><li>Item four</li></ul>
    

    As you can see in the output of curl, no template are used, the output start with the first echo of the handler... Maybe it as something to do with the default template (a slighty modified theme based on the "Boilerplate" from theme-builder) ?

    But if I remove any flush() of the handler, the output correctly use the theme:

    $ curl -i http://elefant.local/demo/stream2
    HTTP/1.1 200 OK
    Date: Tue, 21 May 2013 17:07:00 GMT
    Server: Apache/2.2.22 (Ubuntu)
    X-Powered-By: PHP/5.4.15-1~precise+1
    Set-Cookie: PHPSESSID=0h3pmu2786cf2tk48clu5nu3m1; expires=Wed, 08-Nov-2056 10:14:00 GMT; path=/; domain=elefant.local
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Vary: Accept-Encoding
    Content-Length: 2910
    Content-Type: text/html
    
    <!doctype html>
    <!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="fr"> <![endif]-->
    <!--[if IE 7]>    <html class="no-js ie7 oldie" lang="fr"> <![endif]-->
    <!--[if IE 8]>    <html class="no-js ie8 oldie" lang="fr"> <![endif]-->
    <!--[if gt IE 8]><!--> <html class="no-js" lang="fr"> <!--<![endif]-->
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        (...)
        </head>
    <body>
        <div id="header-container">
            <header class="wrapper clearfix">
                <h1 id="title">Elefant CMS</h1>
    
                <nav>
                    <ul><li><a href="/">Accueil</a></li>
    <li><a href="/blog">Blog</a></li>
    </ul>           </nav>
            </header>
        </div>
    
        <div id="main-container">
            <div id="main" class="wrapper clearfix">
                <article>
    
    
                    <section>
                        <ul><li>Item one</li><li>Item two</li><li>Item three</li><li>Item four</li></ul>
                    </section>
                </article>
    
                <aside>
                    (...)
                    </aside>
    
            </div> <!-- #main -->
    
        </div> <!-- #main-container -->
    
        <div id="footer-container">
            <footer class="wrapper">
                (...)
            </footer>
        </div>
    
    
    </body>
    </html>
    

    Maybe this whole Chunked Transfer Encoding stuff is worth an example by it's own in the examples app you provide ?

  • This is strange, because the Controller::handle() method should actually be calling $this->flush (null); for you:

    https://github.com/jbroadway/elefant/blob/master/lib/Controller.php#L450-L454

    If it's hitting the layout template, then that's obviously being missed. Even more strange, your first example works correctly for me, and gzip encoding shouldn't make any difference either since the call to $this->flush (null); also exits:

    https://github.com/jbroadway/elefant/blob/master/lib/Controller.php#L839-L847

    Can you try adding a line before the if ($this->chunked) { around line 450 of lib/Controller.php that says:

    var_dump ($this->chunked); exit;
    

    Let's see if that's set to true or not and go from there.

  • Started out with a fresh clone of your repository, just added a demo app with a stream.php handler as the doc example.

    When dumping at line 451, $this->chunked is true.

    t$ curl http://elefant-dev.local/demo/stream
    15
    <ul><li>Item one</li>
    11
    <li>Item two</li>
    13
    <li>Item three</li>
    12
    <li>Item four</li>
    6
    </ul>
    

    But more interesting, using the --raw parameter to curl, here is what happening:

    $ curl --raw http://elefant-dev.local/demo/stream
    6E
    15
    <ul><li>Item one</li>
    11
    <li>Item two</li>
    13
    <li>Item three</li>
    12
    <li>Item four</li>
    6
    </ul>
    
    
    0
    

    The initial output (15,11,13,12,6) is chunked again as one chunk (6e) then the zero length final.

  • Finally, found a fix that can be generally applicable, i think... Any tester ?

    https://github.com/jbroadway/elefant/pull/192

  • Looks like that works well over here too, thanks!

Sign In or Register to comment.