From cad84afc6d1b07f6c2dffbe2c3b26c5915f936ae Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 11 Jun 2020 16:22:07 -0400 Subject: [PATCH 1/3] Drop old application/sbt-x1 protocol --- .../sbt/internal/server/NetworkChannel.scala | 141 +++++------------- 1 file changed, 39 insertions(+), 102 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index ef725abff..cbf825797 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -20,7 +20,6 @@ import sbt.internal.protocol.{ JsonRpcResponseMessage } import sbt.internal.util.ObjectEvent -import sbt.internal.util.codec.JValueFormats import sbt.internal.util.complete.Parser import sbt.protocol._ import sbt.util.Logger @@ -52,10 +51,6 @@ final class NetworkChannel( private val ContentLength = """^Content\-Length\:\s*(\d+)""".r private val ContentType = """^Content\-Type\:\s*(.+)""".r private var _contentType: String = "" - private val SbtX1Protocol = "application/sbt-x1" - private val VsCode = sbt.protocol.Serialization.VsCode - private val VsCodeOld = "application/vscode-jsonrpc; charset=utf8" - private lazy val jsonFormat = new sjsonnew.BasicJsonProtocol with JValueFormats {} private val pendingRequests: mutable.Map[String, JsonRpcRequestMessage] = mutable.Map() private lazy val callback: ServerCallback = new ServerCallback { @@ -135,7 +130,8 @@ final class NetworkChannel( // handle un-framing state match { case SingleLine => - tillEndOfLine match { + val line = tillEndOfLine + line match { case Some(chunk) => chunk.headOption match { case None => // ignore blank line @@ -215,60 +211,43 @@ final class NetworkChannel( } def handleBody(chunk: Vector[Byte]): Unit = { - if (isLanguageServerProtocol) { - Serialization.deserializeJsonMessage(chunk) match { - case Right(req: JsonRpcRequestMessage) => - try { - registerRequest(req) - onRequestMessage(req) - } catch { - case LangServerError(code, message) => - log.debug(s"sending error: $code: $message") - respondError(code, message, Some(req.id)) - } - case Right(ntf: JsonRpcNotificationMessage) => - try { - onNotification(ntf) - } catch { - case LangServerError(code, message) => - logMessage("error", s"Error $code while handling notification: $message") - } - case Right(msg) => - log.debug(s"Unhandled message: $msg") - case Left(errorDesc) => - logMessage( - "error", - s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): $errorDesc" - ) - } - } else { - contentType match { - case SbtX1Protocol => - Serialization - .deserializeCommand(chunk) - .fold( - errorDesc => - logMessage( - "error", - s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): " + errorDesc - ), - onCommand - ) - case _ => - logMessage( - "error", - s"Unknown Content-Type: $contentType" - ) - } - } // if-else + Serialization.deserializeJsonMessage(chunk) match { + case Right(req: JsonRpcRequestMessage) => + try { + registerRequest(req) + onRequestMessage(req) + } catch { + case LangServerError(code, message) => + log.debug(s"sending error: $code: $message") + respondError(code, message, Some(req.id)) + } + case Right(ntf: JsonRpcNotificationMessage) => + try { + onNotification(ntf) + } catch { + case LangServerError(code, message) => + logMessage("error", s"Error $code while handling notification: $message") + } + case Right(msg) => + log.debug(s"Unhandled message: $msg") + case Left(errorDesc) => + logMessage( + "error", + s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): $errorDesc" + ) + } } def handleHeader(str: String): Option[Unit] = { + val sbtX1Protocol = "application/sbt-x1" str match { case ContentLength(len) => contentLength = len.toInt Some(()) case ContentType(ct) => + if (ct == sbtX1Protocol) { + logMessage("error", s"server protocol $ct is no longer supported") + } setContentType(ct) Some(()) case _ => None @@ -277,12 +256,7 @@ final class NetworkChannel( } thread.start() - private[sbt] def isLanguageServerProtocol: Boolean = { - contentType match { - case "" | VsCode | VsCodeOld => true - case _ => false - } - } + private[sbt] def isLanguageServerProtocol: Boolean = true private def registerRequest(request: JsonRpcRequestMessage): Unit = { this.synchronized { @@ -328,42 +302,20 @@ final class NetworkChannel( } private[sbt] def notifyEvent[A: JsonFormat](method: String, params: A): Unit = { - if (isLanguageServerProtocol) { - jsonRpcNotify(method, params) - } else { - () - } + jsonRpcNotify(method, params) } def respond[A: JsonFormat](event: A): Unit = respond(event, None) def respond[A: JsonFormat](event: A, execId: Option[String]): Unit = { - if (isLanguageServerProtocol) { - respondResult(event, execId) - } else { - contentType match { - case SbtX1Protocol => - val bytes = Serialization.serializeEvent(event) - publishBytes(bytes, true) - case _ => - } - } + respondResult(event, execId) } def notifyEvent(event: EventMessage): Unit = { - if (isLanguageServerProtocol) { - event match { - case entry: LogEvent => logMessage(entry.level, entry.message) - case entry: ExecStatusEvent => logMessage("debug", entry.status) - case _ => () - } - } else { - contentType match { - case SbtX1Protocol => - val bytes = Serialization.serializeEventMessage(event) - publishBytes(bytes, true) - case _ => () - } + event match { + case entry: LogEvent => logMessage(entry.level, entry.message) + case entry: ExecStatusEvent => logMessage("debug", entry.status) + case _ => () } } @@ -372,22 +324,7 @@ final class NetworkChannel( * erased because it went through logging. */ private[sbt] def respond(event: ObjectEvent[_]): Unit = { - import sjsonnew.shaded.scalajson.ast.unsafe._ - if (isLanguageServerProtocol) onObjectEvent(event) - else { - import jsonFormat._ - val json: JValue = JObject( - JField("type", JString(event.contentType)), - Seq(JField("message", event.json), JField("level", JString(event.level.toString))) ++ - (event.channelName map { channelName => - JField("channelName", JString(channelName)) - }) ++ - (event.execId map { execId => - JField("execId", JString(execId)) - }): _* - ) - respond(json, event.execId) - } + onObjectEvent(event) } def publishBytes(event: Array[Byte]): Unit = publishBytes(event, false) From 033ff1d8a514fced55c99e9de2abe6b336f316ff Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 11 Jun 2020 20:31:13 -0400 Subject: [PATCH 2/3] Make JSON parsing errors more consistent --- .../sbt/internal/server/NetworkChannel.scala | 17 ++++++++++------- .../main/scala/sbt/protocol/Serialization.scala | 13 +++++++------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index cbf825797..b3bd4e644 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -145,7 +145,10 @@ final class NetworkChannel( case Some(_) => state = InHeader process() - case _ => log.error("Got invalid chunk from client: " + str) + case _ => + val msg = s"got invalid chunk from client: $str" + log.error(msg) + logMessage("error", msg) } } case _ => () @@ -226,15 +229,15 @@ final class NetworkChannel( onNotification(ntf) } catch { case LangServerError(code, message) => - logMessage("error", s"Error $code while handling notification: $message") + logMessage("error", s"error $code while handling notification: $message") } case Right(msg) => - log.debug(s"Unhandled message: $msg") + log.debug(s"unhandled message: $msg") case Left(errorDesc) => - logMessage( - "error", - s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): $errorDesc" - ) + val msg = + s"got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): $errorDesc" + log.error(msg) + logMessage("error", msg) } } diff --git a/protocol/src/main/scala/sbt/protocol/Serialization.scala b/protocol/src/main/scala/sbt/protocol/Serialization.scala index 75ef805ca..86d87f6ca 100644 --- a/protocol/src/main/scala/sbt/protocol/Serialization.scala +++ b/protocol/src/main/scala/sbt/protocol/Serialization.scala @@ -175,22 +175,23 @@ object Serialization { if ((fields find { _.field == "id" }).isDefined) Converter.fromJson[JsonRpcRequestMessage](json) match { case Success(request) => Right(request) - case Failure(e) => Left(s"Conversion error: ${e.getMessage}") + case Failure(e) => Left(s"conversion error: ${e.getMessage}") } else Converter.fromJson[JsonRpcNotificationMessage](json) match { case Success(notification) => Right(notification) - case Failure(e) => Left(s"Conversion error: ${e.getMessage}") + case Failure(e) => Left(s"conversion error: ${e.getMessage}") } - } else + } else if ((fields find { _.field == "id" }).isDefined) Converter.fromJson[JsonRpcResponseMessage](json) match { case Success(res) => Right(res) - case Failure(e) => Left(s"Conversion error: ${e.getMessage}") + case Failure(e) => Left(s"conversion error: ${e.getMessage}") } + else Left(s"expected JSON-RPC object but found ${new String(bytes.toArray, "UTF-8")}") case Success(json) => - Left(s"Expected JSON object but found $json") + Left(s"expected JSON object but found ${new String(bytes.toArray, "UTF-8")}") case Failure(e) => - Left(s"Parse error: ${e.getMessage}") + Left(s"parse error: ${e.getMessage}") } } From d599ae2c162f796e1446c64cbc9b6fc5cf969a3e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 12 Jun 2020 15:33:53 -0400 Subject: [PATCH 3/3] Deprecate unused methods --- protocol/src/main/scala/sbt/protocol/Serialization.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocol/src/main/scala/sbt/protocol/Serialization.scala b/protocol/src/main/scala/sbt/protocol/Serialization.scala index 86d87f6ca..6968f2912 100644 --- a/protocol/src/main/scala/sbt/protocol/Serialization.scala +++ b/protocol/src/main/scala/sbt/protocol/Serialization.scala @@ -25,11 +25,13 @@ import sbt.internal.protocol.{ object Serialization { private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8" + @deprecated("unused", since = "1.4.0") def serializeEvent[A: JsonFormat](event: A): Array[Byte] = { val json: JValue = Converter.toJson[A](event).get CompactPrinter(json).getBytes("UTF-8") } + @deprecated("unused", since = "1.4.0") def serializeCommand(command: CommandMessage): Array[Byte] = { import codec.JsonProtocol._ val json: JValue = Converter.toJson[CommandMessage](command).get @@ -99,6 +101,7 @@ object Serialization { /** * @return A command or an invalid input description */ + @deprecated("unused", since = "1.4.0") def deserializeCommand(bytes: Seq[Byte]): Either[String, CommandMessage] = { val buffer = ByteBuffer.wrap(bytes.toArray) Parser.parseFromByteBuffer(buffer) match { @@ -116,6 +119,7 @@ object Serialization { /** * @return A command or an invalid input description */ + @deprecated("unused", since = "1.4.0") def deserializeEvent(bytes: Seq[Byte]): Either[String, Any] = { val buffer = ByteBuffer.wrap(bytes.toArray) Parser.parseFromByteBuffer(buffer) match { @@ -152,6 +156,7 @@ object Serialization { /** * @return A command or an invalid input description */ + @deprecated("unused", since = "1.4.0") def deserializeEventMessage(bytes: Seq[Byte]): Either[String, EventMessage] = { val buffer = ByteBuffer.wrap(bytes.toArray) Parser.parseFromByteBuffer(buffer) match {