jove.notebook.services.Kernel.scala Maven / Gradle / Ivy
The newest version!
package jove
package notebook
package services
import jove.MessageSocket.Channel
import jove.notebook.components.{NotebookSession, Session, KernelWS}
import jove.protocol.Output.ConnectReply
import org.http4s.websocket.WebsocketBits._
import scalaz._, Scalaz._
import scalaz.concurrent.Task
import scalaz.stream.{Sink, Process}
import org.http4s.dsl._
import org.http4s.server.websocket._
import argonaut._, Argonaut._
object Kernel extends LazyLogging {
object ChannelStr {
def unapply(s: String): Option[Channel] =
s match {
case "iopub" =>
Some(Channel.Publish)
case "shell" =>
Some(Channel.Requests)
case "hb" =>
Some(Channel.Heartbeat)
case "stdin" =>
Some(Channel.Stdin)
case "control" =>
Some(Channel.Control)
case _ =>
None
}
}
def apply(kernelWs: KernelWS, session: Session): Plan = {
case req @ (GET -> Root / "api" / "kernels" / kernelId / ChannelStr(channel)) =>
logger info s"WS at $kernelId, $channel requested (query string: ${req.params})"
val res =
for {
sessionId <- req.params.get("session_id") toRightDisjunction new Exception("No session specified")
c <- get(kernelId, sessionId, channel, kernelWs, session)
} yield {
WS(c._1, c._2)
}
res.leftMap{ e =>
logger error s"Launching kernel: $e (${e.getStackTrace mkString "\n"})"
throw e
}.merge
}
def get(kernelId: String, sessionId: String, channel: Channel, kernelWs: KernelWS, session: Session): Throwable \/ (Process[Task, WebSocketFrame], Sink[Task, WebSocketFrame]) = {
logger info s"WS at $kernelId, $channel requested"
def sinkFor(_process: RawMessage => Unit): Sink[Task, WebSocketFrame] =
if (channel == Channel.Requests)
Process.constant {
case Text(t, _) =>
Task {
t.parse match {
case -\/(err) =>
logger warn s"Error while parsing $t on $channel: $err"
case \/-(msg) =>
logger info s"Received msg $msg on $channel"
_process(RawMessage(
Nil,
(msg.hcursor --\ "header").focus.fold("")(_.spaces2),
(msg.hcursor --\ "parent_header").focus.fold("")(_.spaces2),
(msg.hcursor --\ "metadata").focus.fold("")(_.spaces2),
(msg.hcursor --\ "content").focus.fold("")(_.spaces2)
))
}
}
case other =>
Task {
logger warn s"Message with unknown type on $channel: $other"
}
}
else
Process.constant {
case msg =>
Task {
logger warn s"Got unexpected message on channel $channel: $msg"
}
}
session.sessionOrNew(kernelId, sessionId) map {
case NotebookSession.EmbeddedNotebookSession(_, _, interpreter) =>
val sink =
sinkFor(Handler(
kernelWs.socket(sessionId),
ConnectReply(-1, -1, -1, -1),
interpreter
))
(kernelWs.process(sessionId, channel).map{ m =>
val m0 = m.protocolUp.toJsonStr
logger info s"Sending $m0 to browser"
Text(m0)
}, sink)
case NotebookSession.RemoteNotebookSession(_, _, remoteSocket) =>
val sink = sinkFor(remoteSocket.send(channel, _))
(kernelWs.remoteProcess(remoteSocket, sessionId, channel).map { m =>
val m0 = m.protocolUp.toJsonStr
logger info s"Sending $m0 to browser"
Text(m0)
}, sink)
}
}
}