Dlaczego wywołanie błędu lub wykonanie w Iteratee BodyParsera powoduje zawieszenie żądania w Play Framework 2.0?

Próbuję zrozumieć reaktywne koncepcje we / wy w środowisku Play 2.0. Aby uzyskać lepsze zrozumienie od samego początku, postanowiłem pominąć pomocników frameworka, aby skonstruować różne rodzaje iteracji i napisać niestandardowyIteratee od podstaw do wykorzystania przezBodyParser analizować treść żądania.

Począwszy od informacji dostępnych wIteratees iScalaBodyParser docs i dwie prezentacje na temat gry reaktywnych I / O to właśnie wymyśliłem:

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 => new SomeIteratee)) { request =>
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]]) => Promise[B],
12     cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B],
13     error: (String, Input[Array[Byte]]) => Promise[B]
14   ): Promise[B] = state match {
15     case 'Done => { println("Done"); done(Right(received), Input.Empty) }
16     case 'Cont => cont(in => in match {
17       case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length)
18       case Empty => copy(input = in)
19       case EOF => copy(state = 'Done, input = in)
20       case _ => copy(state = 'Error, input = in)
21     })
22     case _ => { println("Error"); error("Some error.", input) }
23   }
24 }

(Uwaga:Wszystko te rzeczy są dla mnie nowe, więc proszę wybaczyć, jeśli coś w tym jest całkowitym gównem.) Iteratee jest dość głupie, po prostu odczytuje wszystkie kawałki, sumuje liczbę odebranych bajtów i wypisuje niektóre wiadomości. Wszystko działa zgodnie z oczekiwaniami, gdy wywołam akcję kontrolera z niektórymi danymi - mogę obserwować, jak wszystkie porcje są odbierane przez Iteratee i kiedy wszystkie dane są odczytywane, przełącza się na stan zrobiony i żądanie się kończy.

Teraz zacząłem bawić się kodem, ponieważ chciałem zobaczyć zachowanie tych dwóch przypadków:

Przełączanie na błąd stanu przed odczytem wszystkich danych wejściowych.Przejście do stanu wykonanego przed odczytem wszystkich danych wejściowych i powrotemResult zamiast tegoInt.

Moje rozumienie wyżej wymienionej dokumentacji jest takie, że oba powinny być możliwe, ale w rzeczywistości nie jestem w stanie zrozumieć obserwowanego zachowania. Aby przetestować pierwszy przypadek, zmieniłam wiersz 17 powyższego kodu na:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)

Właśnie dodałem warunek, aby przejść do stanu błędu, jeśli odebrano więcej niż 10000 bajtów. Otrzymuję wynik:

'Cont Empty 0
'Cont El([B@38ecece6) 8192
'Error El([B@4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error

Wtedy prośba zawiesza się na zawsze i nigdy się nie kończy. Moje oczekiwanie od wyżej wymienionych doktorów było takie, że kiedy dzwonięerror funkcjonować wewnątrzfold przetwarzania Iteratee należy przerwać. Dzieje się tak dlatego, że metoda składania Iteratee jest wywoływana kilka razy poerror został wywołany - cóż, a następnie żądanie zawiesza się.

Kiedy przełączam się do stanu gotowości przed odczytaniem wszystkich danych wejściowych, zachowanie jest bardzo podobne. Zmiana linii 15 na:

15    case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }

i wiersz 17 do:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)

produkuje następujące dane wyjściowe:

'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)

i znowu prośba wisi na zawsze.

Moje główne pytanie brzmi: dlaczego prośba jest zawieszona w wyżej wymienionych przypadkach. Gdyby ktoś mógł rzucić światło na to, byłbym bardzo wdzięczny!

questionAnswers(2)

yourAnswerToTheQuestion