2012-07-13 8 views
5

Estoy tratando de comprender los conceptos de E/S reactivas del framework Play 2.0. Con el fin de obtener una mejor comprensión desde el principio, decidí omitir los helpers del framework para construir iteratees de diferentes tipos y escribir un Iteratee personalizado desde cero para ser utilizado por un BodyParser para analizar un cuerpo de solicitud.¿Por qué hace que el error de llamada o hecho en un Iteratee de BodyParser cuelgue la solicitud en Play Framework 2.0?

A partir de la información disponible en Iteratees y ScalaBodyParser documentos y dos presentaciones sobre el juego que reactiva/O esto es lo que ocurrió:

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 } 

(Observación: Todo estas cosas son nuevas para mí , por favor, perdona si algo sobre esto es una mierda total.) El Iteratee es bastante tonto, solo lee todos los fragmentos, resume el número de bytes recibidos e imprime algunos mensajes. Todo funciona como se espera cuando llamo a la acción del controlador con algunos datos: puedo observar que todos los fragmentos son recibidos por Iteratee y cuando se leen todos los datos, pasa al estado finalizado y la solicitud finaliza.

Ahora empecé a jugar con el código porque quería ver el comportamiento de estos dos casos:

  • conmutación en el error de estado antes de leer toda la entrada.
  • Cambiando al estado anterior a la lectura de todas las entradas y devolviendo Result en lugar de Int.

Mi comprensión de la documentación mencionada anteriormente es que ambas deberían ser posibles, pero en realidad no puedo entender el comportamiento observado. Para probar el primer caso cambié la línea 17 del código anterior para:

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

Así que sólo añade una condición para cambiar al estado de error si se recibieron más de 10000 bytes. La salida que obtengo es la siguiente:

'Cont Empty 0 
'Cont El([[email protected]) 8192 
'Error El([[email protected]) 16384 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 

Luego la solicitud se cuelga para siempre y nunca termina. Mi expectativa de los documentos mencionados anteriormente fue que cuando llamo a la función error dentro de fold de un Iteratee el proceso debe detenerse. Lo que está sucediendo aquí es que el método de plegado de Iteratee se llama varias veces después de que se haya llamado al error, y luego se cuelga la solicitud.

Cuando cambio al estado hecho antes de leer todas las entradas, el comportamiento es bastante similar. Cambio de la línea 15 a:

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

y la línea 17 a:

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

produce el siguiente resultado:

'Cont Empty 0 
'Cont El([[email protected]) 8192 
'Done El([[email protected]) 16384 
Done with El([[email protected]) 
Done with El([[email protected]) 
Done with El([[email protected]) 
Done with El([[email protected]) 

y otra vez la solicitud cuelga siempre.

Mi pregunta principal es por qué la solicitud se cuelga en los casos mencionados anteriormente. ¡Si alguien pudiera arrojar luz sobre esto, lo agradecería enormemente!

Respuesta

0

Las cosas deben haber cambiado con Play 2.1 - La promesa ya no es paramétrica, y este ejemplo ya no se compila.

Cuestiones relacionadas