///|
struct LspFramer {
mut buffer : Bytes
mut length : Int?
mut closed : Bool
}
///|
pub(all) enum LspPacket {
LspRequest(Json)
LspInvalidJson
}
///|
pub fn new_lsp_framer() -> LspFramer {
{ buffer: b"", length: None, closed: false, }
}
///|
pub fn lsp_frame(message : Json) -> Bytes {
let body = @utf8.encode(message.stringify()[:])
@utf8.encode(("Content-Length: " + body.length().to_string() + "\r\n\r\n")[:]) +
body
}
///|
pub fn LspFramer::feed(
self : LspFramer,
chunk : Bytes,
) -> Array[LspPacket] raise {
if self.closed {
raise Failure("LSP framer is closed")
}
errdefer {
self.closed = true
}
if chunk.length() > 16842752 ||
self.buffer.length() > 16846848 - chunk.length() {
raise Failure("LSP input exceeds frame budget")
}
self.buffer = self.buffer + chunk
let messages : Array[LspPacket] = []
while true {
if self.length is None {
let mut end = -1
let bytes = self.buffer
for i in 0..<(bytes.length() - 3).max(0) {
if bytes[i] == 13 &&
bytes[i + 1] == 10 &&
bytes[i + 2] == 13 &&
bytes[i + 3] == 10 {
end = i
break
}
}
if end < 0 {
if bytes.length() > 4096 {
raise Failure("LSP header exceeds 4096 bytes")
}
break
}
if end > 4096 {
raise Failure("LSP header exceeds 4096 bytes")
}
let header = @utf8.decode(bytes[:end])
let mut length : Int? = None
for line in header.split("\r\n") {
guard line.find(":") is Some(colon) else {
raise Failure("Malformed LSP header")
}
let name = line[:colon].to_owned()
if name.equal_ignore_ascii_case("Content-Length") {
if length is Some(_) {
raise Failure("Duplicate Content-Length")
}
let value = line[colon + 1:].trim().to_owned()
if !decimal_id(value) {
raise Failure("Invalid Content-Length")
}
let count = @strconv.parse_int(value[:])
if count < 1 || count > 16777216 {
raise Failure("LSP message exceeds 16 MiB")
}
length = Some(count)
}
}
if length is None {
raise Failure("Missing Content-Length")
}
self.length = length
self.buffer = bytes[end + 4:].to_owned()
}
let count = self.length.unwrap()
if self.buffer.length() < count {
break
}
let message = try {
let text = @utf8.decode(self.buffer[:count], ignore_bom=false)
LspRequest(@json.parse(text[:]))
} catch {
_ => LspInvalidJson
}
messages.push(message)
self.buffer = self.buffer[count:].to_owned()
self.length = None
}
messages
}
///|
pub fn LspFramer::finish(self : LspFramer) -> Unit raise {
if self.closed {
raise Failure("LSP framer is closed")
}
self.closed = true
if !self.buffer.is_empty() || self.length is Some(_) {
raise Failure("Incomplete LSP frame")
}
}