Sto cercando di capire i concetti di I/O reattivo del framework Play 2.0. Per capire meglio fin dall'inizio, ho deciso di saltare gli helper del framework per costruire iterate di diverso tipo e di scrivere uno Iteratee
personalizzato da utilizzare da un BodyParser
per analizzare il corpo di una richiesta.Perché rende l'errore di chiamata o fatto in un Iteratee di BodyParser la richiesta si blocca in Play Framework 2.0?
partire con le informazioni disponibili in Iteratees e ScalaBodyParser documenti e due presentazioni su gioco I reattiva/O questo è ciò che mi si avvicinò con:
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 }
(Nota: Tutti queste cose sono nuove per me , per favore perdonami se qualcosa su questo è una schifezza totale.) L'Iteratee è piuttosto stupido, legge solo tutti i blocchi, somma il numero di byte ricevuti e stampa alcuni messaggi. Tutto funziona come previsto quando chiamo l'azione del controller con alcuni dati: posso osservare che tutti i blocchi vengono ricevuti dall'Iteratee e quando tutti i dati vengono letti, passa allo stato completato e la richiesta termina.
Ora ho iniziato a giocare con il codice perché volevo vedere il comportamento di questi due casi:
- commutazione in errore stato precedente tutti gli input viene letto.
- Passaggio allo stato eseguito prima che tutti gli input vengano letti e restituendo un
Result
anziché loInt
.
La mia comprensione della documentazione di cui sopra è che entrambi dovrebbero essere possibili ma in realtà non sono in grado di comprendere il comportamento osservato. Per testare il primo caso ho cambiato linea 17 del codice precedente:
17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)
Così ho aggiunto un condizione di passare nello stato di errore se sono state ricevute più di 10000 byte. L'output che ottengo è questo:
'Cont Empty 0
'Cont El([[email protected]) 8192
'Error El([[email protected]) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Quindi la richiesta si blocca per sempre e non finisce mai. La mia aspettativa dai documenti sopra citati era che quando chiamo la funzione error
all'interno di fold
di un Iteratee l'elaborazione dovrebbe essere interrotta. Quello che sta accadendo qui è che il metodo fold di Iteratee viene chiamato più volte dopo che è stato chiamato error
- beh e quindi la richiesta si blocca.
Quando si passa allo stato di fatto prima di leggere tutti gli input, il comportamento è abbastanza simile. Modificando la riga 15 a:
15 case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }
e la linea 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 il seguente output:
'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])
e nuovamente la richiesta si blocca sempre.
La mia domanda principale è perché la richiesta è sospesa nei casi sopra citati. Se qualcuno potesse far luce su questo, lo apprezzerei molto!
Grazie per la correzione e le parole gentili, proverà quella correzione al più presto! – lost