streams.clj

Bowmap · bowmap.git · Tickets

Branches > main > e81f338 > 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)))))