Warum hängt die Anforderung in Play Framework 2.0, wenn ein Aufruffehler auftritt oder wenn die Anforderung in einem BodyParser-Iterat ausgeführt wird?

Ich versuche, die reaktiven E / A-Konzepte des Play 2.0-Frameworks zu verstehen. Um von Anfang an ein besseres Verständnis zu erlangen, habe ich mich entschlossen, die Helfer des Frameworks zu überspringen, um Iteratees verschiedener Art zu konstruieren und einen Custom zu schreibenIteratee von Grund auf neu von einem verwendet werdenBodyParser einen Anfragetext analysieren.

Beginnend mit den Informationen inIterierte undScalaBodyParser Dokumente und zwei Präsentationen zu reaktiven I / O-Vorgängen:

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 }

(Anmerkung:Alles Diese Dinge sind für mich neu, also bitte verzeihen Sie, wenn irgendetwas davon total beschissen ist.) Der Iteratee ist ziemlich dumm, liest einfach alle Chunks, summiert die Anzahl der empfangenen Bytes und druckt einige Nachrichten aus. Alles funktioniert wie erwartet, wenn ich die Controller-Aktion mit einigen Daten aufrufe. Ich kann beobachten, dass alle Chunks vom Iterierten empfangen werden, und wenn alle Daten gelesen werden, wechselt sie in den Status "erledigt" und die Anforderung endet.

Jetzt fing ich an, mit dem Code herumzuspielen, weil ich das Verhalten für diese beiden Fälle sehen wollte:

In den Status Fehler wechseln, bevor alle Eingaben gelesen werden.Schalten Sie in den Status "Fertig", bevor alle Eingaben gelesen und zurückgegeben werden. AResult anstatt derInt.

Nach meinem Verständnis der oben genannten Dokumentation sollte beides möglich sein, aber ich kann das beobachtete Verhalten nicht verstehen. Um den ersten Fall zu testen, habe ich Zeile 17 des obigen Codes folgendermaßen geändert:

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

Deshalb habe ich eine Bedingung hinzugefügt, um in den Fehlerzustand zu wechseln, wenn mehr als 10000 Bytes empfangen wurden. Die Ausgabe, die ich erhalte, ist die folgende:

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

Dann hängt die Anfrage für immer und endet nie. Meine Erwartung an die oben genannten Dokumente war, dass ich die anrufeerror Funktion im Innerenfold eines Iterierten sollte die Verarbeitung gestoppt werden. Was hier passiert, ist, dass die Fold-Methode des Iterierten mehrmals aufgerufen wirderror wurde angerufen - na und dann hängt die Anfrage.

Wenn ich vor dem Lesen aller Eingaben in den Status "Fertig" wechsle, ist das Verhalten ziemlich ähnlich. Ändern Sie Zeile 15 in:

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

und Zeile 17 an:

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

erzeugt die folgende Ausgabe:

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

und wieder hängt die Anfrage für immer.

Meine Hauptfrage ist, warum die Anfrage in den oben genannten Fällen hängt. Wenn jemand Licht ins Dunkel bringen könnte, würde ich es sehr schätzen!

Antworten auf die Frage(2)

Ihre Antwort auf die Frage