streams.clj
Branches > main > latest > files / src / net / bowmap / server / network / streams.clj
(ns net.bowmap.server.network.streams
"Functions for reading and writing frames to Input and OutputStreams"
(:require
[net.bowmap.server.packet.frame :as frame]
[net.bowmap.server.packet.varint :as varint])
(:import
[java.io IOException InputStream OutputStream]))
(defn read-byte!!
"Blocks and reads an unsigned byte (int) from an InputStream.
Throws if EOF is reached."
[input-stream]
(let [x (InputStream/.read input-stream)]
(if (= x -1)
(throw (IOException. "EOF while reading from InputStream"))
x)))
(def max-frame-bytes 2097151)
(defn read-varint!!
"Blocks and reads a varint from an InputStream.
Throws if EOF is reached, if the encoded varint is invalid, or if it is
not a valid frame length."
[input-stream]
(let [n (varint/decode-varint read-byte!! input-stream)]
(if (<= 0 n max-frame-bytes)
n
(throw (IOException. (str "Invalid frame length: " n))))))
(defn read-frame!!
"Blocks and reads a frame from an InputStream.
Throws if EOF is reached, or if the encoded frame is invalid."
[input-stream]
(let [len (read-varint!! input-stream)
arr (byte-array len)]
(loop [offset 0]
(when (< offset len)
(let [r (InputStream/.read input-stream arr offset (- len offset))]
(if (= r -1)
(throw (IOException. "EOF while reading frame"))
(recur (+ offset r))))))
(frame/frame arr)))
(defn write-varint!!
"Blocks and writes a varint to an OutputStream."
[output-stream n]
(varint/encode-varint (fn [output-stream b]
(doto output-stream
(^[int] OutputStream/.write b)))
output-stream n))
(defn write-frame!!
"Blocks and writes a frame to an OutputStream.
Throws if the frame cannot be written."
[frame output-stream]
(let [^bytes array (frame/array frame)]
(if (> (alength array) max-frame-bytes)
(throw (IOException. "Frame exceeds maximum length."))
(doto output-stream
(write-varint!! (alength array))
(OutputStream/.write array)
(OutputStream/.flush)))))