{"id":18350301,"url":"https://github.com/ollls/netio","last_synced_at":"2025-04-06T10:32:11.041Z","repository":{"id":48133890,"uuid":"516549427","full_name":"ollls/netio","owner":"ollls","description":"TLS  Effects with CATS Effects 3.0 Async JAVA-NIO","archived":false,"fork":false,"pushed_at":"2024-04-19T14:10:37.000Z","size":54,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-21T21:51:11.826Z","etag":null,"topics":["cats","io","java-nio","nio","scala","tls"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ollls.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-21T23:28:10.000Z","updated_at":"2024-05-19T12:41:41.000Z","dependencies_parsed_at":"2024-11-05T21:41:09.540Z","dependency_job_id":null,"html_url":"https://github.com/ollls/netio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollls%2Fnetio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollls%2Fnetio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollls%2Fnetio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollls%2Fnetio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ollls","download_url":"https://codeload.github.com/ollls/netio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247470231,"owners_count":20944143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cats","io","java-nio","nio","scala","tls"],"created_at":"2024-11-05T21:26:01.436Z","updated_at":"2025-04-06T10:32:10.625Z","avatar_url":"https://github.com/ollls.png","language":"Scala","readme":"# netio - TLS Effects for Quartz-H2 HTTP/2 streaming server. \n\nApril 19, 2024 - Update to make it current with quartz-H2 source code. ( primaraly TLS SNI(Server Name Indication) fixes )\u003cbr\u003e\nJul 28, 2022 - Scala 2.13 support in main_2.13 branch ( sbt test fixed as well ). \n\n### 100% async JAVA NIO TCP and TLS interface for Cats Effects 3.\n\u003cbr\u003eTCP operations are composed as CATS Effect Async IO with java.nio.channels.CompletionHandler and all TLS decryption/encryption operations are modeled as ZIO effects with javax.net.ssl.SSLEngine.\nRefer to /test for examples. More documentation will be provided.\n\nAlso you may want to check-out https://github.com/ollls/quartz-h2 ( HTTP2 streamer based on netio on Scala3 )\n\n\n```scala\ntrait IOChannel {\n  def read( timeOut: Int): IO[Chunk[Byte]]\n  def write(buffer: ByteBuffer): IO[Int]\n  def close() : IO[Unit]\n}\n```\n\n\nIOChannel implemented for plain connection with TCPChannel and TLSChannel for TLS connection respectively.\u003cbr\u003e All three methods implement convenient basic protocol which is meant to be used together with fs2.Stream. At the code level TCP or TLS happens transparently, after relvant channel initialization at the beginning.\n\nUse cases:\u003cbr\u003e\n\n* Here is example for incoming HTTP2 packet reader with fs2.Stream with netio interface. All it does it spits incomimg arbitatry data blocks into fs2 stream of HTTP2 packets represented as fs2.Chunk[Byte].\n\n```scala\nprivate[this] def makePacketStream(\n      ch: IOChannel,\n      MAX_FRAME_SIZE: Int,\n      leftOver: Chunk[Byte]\n  ): Stream[IO, Chunk[Byte]] = {\n    val s0 = Stream.chunk[IO, Byte](leftOver)\n    val s1 =\n      Stream\n        .repeatEval(ch.read(HTTP2_KEEP_ALIVE_MS))\n        .flatMap(c0 =\u003e Stream.chunk(c0))\n\n    def go2(s: Stream[IO, Byte], chunk: Chunk[Byte]): Pull[IO, Byte, Unit] = {\n\n      val bb = chunk.toByteBuffer\n      val len = Frames.getLengthField(bb) + 3 + 1 + 1 + 4\n\n      if (chunk.size \u003e len) {\n        Pull.output[IO, Byte](chunk.take(len)) \u003e\u003e go2(s, chunk.drop(len))\n      } else if (chunk.size == len) Pull.output[IO, Byte](chunk)\n      else { go(s, chunk) }\n    }\n\n    def go(s: Stream[IO, Byte], leftover: Chunk[Byte]): Pull[IO, Byte, Unit] = {\n      s.pull.uncons.flatMap {\n        case Some((hd1, tl)) =\u003e\n          val hd = leftover ++ hd1\n          val bb = hd.toByteBuffer\n          val len = Frames.getLengthField(bb) + 3 + 1 + 1 + 4\n          (if (hd.size == len) { Pull.output[IO, Byte](hd) \u003e\u003e go(tl, Chunk.empty[Byte]) }\n           else if (hd.size \u003e len) {\n             Pull.output[IO, Byte](hd.take(len)) \u003e\u003e go2(tl, hd.drop(len)) \u003e\u003e go(tl, Chunk.empty[Byte])\n           } else {\n             go(tl, hd)\n           })\n\n        case None =\u003e Pull.done\n      }\n    }\n    go(s0 ++ s1, Chunk.empty[Byte]).stream.chunks\n  }\n}\n```\n\n* Simplified HTTP1 Chunked fs2.Stream converter.\n\n```scala\n  private[this] def makeChunkedStream(leftOver: Chunk[Byte]) = {\n    val s0 = Stream.chunk[IO, Byte](leftOver)\n    val s1 = Stream.repeatEval(ch.read(TIMEOUT_MS)).flatMap(c0 =\u003e Stream.chunk(c0))\n\n    def go2(s: Stream[IO, Byte], hd: Chunk[Byte]): Pull[IO, Byte, Unit] = {\n      val (chunkSize, offset) = extractChunkLen2(hd)\n      val len = hd.size - offset // actual data avaialble\n \n      if (chunkSize == 0) Pull.done\n      else if (len == chunkSize + 2)\n        Pull.output[IO, Byte](hd.drop(offset).take(chunkSize)) \u003e\u003e go(\n          s,\n          chunk_drop_with_validation(hd, offset + chunkSize)\n        )\n      else if (len \u003e= chunkSize + 2) // account for chunk end marking\n        Pull.output[IO, Byte](hd.drop(offset).take(chunkSize)) \u003e\u003e go2(\n          s,\n          chunk_drop_with_validation(hd, offset + chunkSize)\n        )\n      else go(s, hd)\n    }\n    \n    \n\n  def extractChunkLen2(db: Chunk[Byte]): (Int, Int) = {\n    var l = List.empty[Char]\n    var c: Byte = 0\n    var i: Int = 0\n    while {\n      { c = db(i); i += 1 }\n      c != '\\r' \u0026\u0026 i \u003c 8 // 8 chars for chunked len\n    } do (l = l.appended(c.toChar))\n    if (c == '\\r' \u0026\u0026 db(i) == '\\n') i += 1\n    else throw (new ChunkedEncodingError(\"\"))\n    (Integer.parseInt(l.mkString, 16), i)\n  }\n\n\n  def chunk_drop_with_validation(data: Chunk[Byte], offset: Int) = {\n    val data2 = data.drop(offset)\n    // validate end of chunk\n    val test = (data2(0) == '\\r' \u0026\u0026 data2(1) == '\\n')\n    if (test == false) throw (new ChunkedEncodingError(\"\"))\n    data2.drop(2) // get rid of end of block markings\n  }\n  \n  ```\n\n\n\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Follls%2Fnetio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Follls%2Fnetio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Follls%2Fnetio/lists"}