ollls / zio-quartz-h2   0.5.7

MIT License GitHub

ZIO2 native, asynchronous Java NIO based implementation of http/2 packet streaming server with TLS encryption implemented as scala ZIO2 effect. Direct native translation of ZIO ZStream chunks into http2 packets (inbound and outbound).

Scala versions: 3.x

Generic badge Quality Gate Status

Asynchronous Java NIO http/2 TLS packet streaming server/client.

It's now with HTTP/1.1

ZIO2 native,asynchronous,Java NIO based implementation of http/2 packet streaming server with TLS encryption implemented as scala ZIO2 effect with ALPN h2 tag. Direct native translation of ZIO ZStream chunks into http2 packets (inbound and outbound). Tested and optimized to produce highest possible TPS. Server supports http multipart with ZStream interface along with automatic file saving for file based multipart uploads.

libraryDependencies += "io.github.ollls" %% "zio-quartz-h2" % "0.5.5"

to run example from zio-quartz-h2 code base directly

sbt IO/run

Logging.

Use .../src/main/resources/logback-test.xml to tailor to your specific requirements.

Also you may use options to control logging level.

sbt "run --debug"
sbt "run --error"
sbt "run --off"

0.5.4 template example: client/server (quartz-h2 HTTP/2 client only supports TLS with ALPN H2 HTTP/2 hosts)

https://github.com/ollls/zio-quartz-demo


Standard support for ZIO Environment.

def run =
    val env = ZLayer.fromZIO( ZIO.succeed( "Hello ZIO World!") )
    (for {
      ctx <- QuartzH2Server.buildSSLContext("TLS", "keystore.jks", "password")
      exitCode <- new QuartzH2Server("localhost", 8443, 16000, ctx).startIO(R, filter, sync = false)

    } yield (exitCode)).provideSomeLayer( env )

Webfilter support with Either[Response, Request]. Provide filter as a parameter QuartzH2Server()

val filter: WebFilter = (request: Request) =>
  ZIO.attempt(
    Either.cond(
     !request.uri.getPath().startsWith("/private"),
     request.hdr("test_tid" -> "ABC123Z9292827"),
     Response.Error(StatusCode.Forbidden).asText("Denied: " + request.uri.getPath())
    )
 )    
exitCode <- new QuartzH2Server("localhost", 8443, 16000, ctx).startIO(R, filter, sync = false)

File retrieval.

case GET -> Root / StringVar(file) =>
      val FOLDER_PATH = "/Users/user000/web_root/"
      val FILE = s"$file"
      val BLOCK_SIZE = 16000
      for {
        jpath <- ZIO.attempt(new java.io.File(FOLDER_PATH + FILE))
        present <- ZIO.attempt(jpath.exists())
        _ <- ZIO.fail(new java.io.FileNotFoundException).when(present == false)

      } yield (Response
        .Ok()
        .asStream(ZStream.fromFile( jpath, BLOCK_SIZE ))
        .contentType(ContentType.contentTypeFromFileName(FILE)))

File upload ( smart http2 flow control implemented if disk saving speed cannot keep up with inbound network data.)

 case req @ POST -> Root / "upload" / StringVar(file) =>
      val FOLDER_PATH = "/Users/user000/web_root/"
      val FILE = s"$file"
      for {
        jpath <- ZIO.attempt(new java.io.File(FOLDER_PATH + FILE))
        u <- req.stream.run(ZSink.fromFile(jpath))
      } yield (Response.Ok().asText("OK"))
        

HTTP Multipart file upload.

 case req @ POST -> Root / "mpart" =>
      MultiPart.writeAll(req, "/Users/user000/tmp1/" ) *> ZIO.succeed(Response.Ok())

How to send data in separate H2 packets of various size

    case GET -> Root / "example" =>
      val ts = ZStream.fromChunks(Chunk.fromArray("Block1\n".getBytes()), Chunk.fromArray("Block22\n".getBytes()))
      ZIO.attempt(Response.Ok().asStream(ts))
      

How to run h2spec.

  1. Start server with "sbt IO/run"
  2. ./h2spec http2 -h localhost -p 8443 -t -k

You should get:

Finished in 2.1959 seconds
94 tests, 94 passed, 0 skipped, 0 failed<br>

Performance test with h2load.

h2load -D10 -c68 -m30 -t2 https://localhost:8443/test

...

finished in 10.01s, 65292.20 req/s, 637.89KB/s
requests: 652922 total, 654452 started, 652922 done, 652922 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 652922 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 6.23MB (6531974) total, 637.62KB (652922) headers (space savings 90.00%), 0B (0) data
                     min         max         mean         sd        +/- sd
time for request:      231us    144.82ms     19.31ms     10.02ms    73.06%
time for connect:     7.39ms    860.22ms    385.69ms    278.36ms    54.90%
time to 1st byte:     9.10ms    875.77ms    398.01ms    282.96ms    54.90%
req/s           :       0.00     1489.99      959.99      563.28    75.00%