Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy makes calling error or done in a BodyParser's Iteratee the request hang in Play Framework 2.0?
    primarykey
    data
    text
    <p>I am trying to understand the reactive I/O concepts of Play 2.0 framework. In order to get a better understanding from the start I decided to skip the framework's helpers to construct iteratees of different kinds and to write a custom <code>Iteratee</code> from scratch to be used by a <code>BodyParser</code> to parse a request body. </p> <p>Starting with the information available in <a href="http://www.playframework.org/documentation/2.0/Iteratees" rel="nofollow">Iteratees</a> and <a href="https://github.com/playframework/Play20/wiki/ScalaBodyParsers" rel="nofollow">ScalaBodyParser</a> docs and two presentations about play reactive I/O this is what I came up with:</p> <pre><code>import play.api.mvc._ import play.api.mvc.Results._ import play.api.libs.iteratee.{Iteratee, Input} import play.api.libs.concurrent.Promise import play.api.libs.iteratee.Input.{El, EOF, Empty} 01 object Upload extends Controller { 02 def send = Action(BodyParser(rh =&gt; new SomeIteratee)) { request =&gt; 03 Ok("Done") 04 } 05 } 06 07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] { 08 println(state + " " + input + " " + received) 09 10 def fold[B]( 11 done: (Either[Result, Int], Input[Array[Byte]]) =&gt; Promise[B], 12 cont: (Input[Array[Byte]] =&gt; Iteratee[Array[Byte], Either[Result, Int]]) =&gt; Promise[B], 13 error: (String, Input[Array[Byte]]) =&gt; Promise[B] 14 ): Promise[B] = state match { 15 case 'Done =&gt; { println("Done"); done(Right(received), Input.Empty) } 16 case 'Cont =&gt; cont(in =&gt; in match { 17 case in: El[Array[Byte]] =&gt; copy(input = in, received = received + in.e.length) 18 case Empty =&gt; copy(input = in) 19 case EOF =&gt; copy(state = 'Done, input = in) 20 case _ =&gt; copy(state = 'Error, input = in) 21 }) 22 case _ =&gt; { println("Error"); error("Some error.", input) } 23 } 24 } </code></pre> <p>(Remark: <em>All</em> these things are new to me, so please forgive if something about this is total crap.) The Iteratee is pretty dumb, it just reads all chunks, sums up the number of received bytes and prints out some messages. Everything works as expected when I call the controller action with some data - I can observe all chunks are received by the Iteratee and when all data is read it switches to state done and the request ends.</p> <p>Now I started to play around with the code because I wanted to see the behaviour for these two cases:</p> <ul> <li>Switching into state error before all input is read.</li> <li>Switching into state done before all input is read and returning a <code>Result</code> instead of the <code>Int</code>.</li> </ul> <p>My understanding of the documentation mentioned above is that both should be possible but actually I am not able to understand the observed behaviour. To test the first case I changed line 17 of the above code to:</p> <pre><code>17 case in: El[Array[Byte]] =&gt; copy(state = if(received + in.e.length &gt; 10000) 'Error else 'Cont, input = in, received = received + in.e.length) </code></pre> <p>So I just added a condition to switch into the error state if more than 10000 bytes were received. The output I get is this:</p> <pre><code>'Cont Empty 0 'Cont El([B@38ecece6) 8192 'Error El([B@4ab50d3c) 16384 Error Error Error Error Error Error Error Error Error Error Error </code></pre> <p>Then the request hangs forever and never ends. My expectation from the above mentioned docs was that when I call the <code>error</code> function inside <code>fold</code> of an Iteratee the processing should be stopped. What is happening here is that the Iteratee's fold method is called several times after <code>error</code> has been called - well and then the request hangs.</p> <p>When I switch into done state before reading all input the behaviour is quite similar. Changing line 15 to:</p> <pre><code>15 case 'Done =&gt; { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) } </code></pre> <p>and line 17 to:</p> <pre><code>17 case in: El[Array[Byte]] =&gt; copy(state = if(received + in.e.length &gt; 10000) 'Done else 'Cont, input = in, received = received + in.e.length) </code></pre> <p>produces the following output:</p> <pre><code>'Cont Empty 0 'Cont El([B@16ce00a8) 8192 'Done El([B@2e8d214a) 16384 Done with El([B@2e8d214a) Done with El([B@2e8d214a) Done with El([B@2e8d214a) Done with El([B@2e8d214a) </code></pre> <p>and again the request hangs forever.</p> <p>My main question is why the request is hanging in the above mentioned cases. If anybody could shed light on this I would greatly appreciate it!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload