Skip to content

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).

License

Notifications You must be signed in to change notification settings

ollls/zio-quartz-h2

Repository files navigation

Generic badge

https://ollls.github.io/zio-quartz-h2-doc/

Asynchronous HTTP/2 TLS packet streaming server/client with multiple I/O options

It now supports HTTP/1.1 and IO-Uring on Linux platforms

ZIO2 native, asynchronous 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 HTTP/2 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.

I/O Implementation Options

  • Java NIO: Cross-platform compatibility with high performance
  • Linux IO-Uring: Native Linux kernel I/O for maximum performance (new in v0.6.0)
  • Synchronous Mode: Traditional blocking Java sockets for specific use cases
libraryDependencies += "io.github.ollls" %% "zio-quartz-h2" % "0.6.0"

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

sbt IO/run

Using IO-Uring on Linux

To utilize the new IO-Uring implementation on Linux platforms:

// Configure the number of IO-Uring instances
val NUMBER_OF_RING_INSTANCES = 1

// Recommended ZIO Runtime configuration for IO-Uring
override val bootstrap = zio.Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ zio.Runtime.setExecutor(
  zio.Executor.fromJavaExecutor(Executors.newWorkStealingPool(Runtime.getRuntime().availableProcessors() - NUMBER_OF_RING_INSTANCES))
) ++ zio.Runtime.setBlockingExecutor(zio.Executor.fromJavaExecutor(Executors.newCachedThreadPool()))

// Start server with IO-Uring
exitCode <- new QuartzH2Server(
  "localhost",
  8443,
  16000,
  ctx,
  onConnect = onConnect,
  onDisconnect = onDisconnect
).startIO_linuxOnly(NUMBER_OF_RING_INSTANCES, R, filter)

For a complete working example of IO-Uring implementation, refer to the examples/IOU/src/main/scala/Run.scala file in the repository.

For a complete working example of Java NIO implementation, refer to the examples/IO/src/main/scala/Run.scala file in the repository.

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"

Version Information

  • Current Version: 0.6.0
  • Major Changes: Added Linux IO-Uring support, upgraded to Scala 3.3, updated logback-classic to 1.5.17

Template Examples


Features

  • HTTP/2 with TLS encryption and ALPN h2 tag
  • HTTP/1.1 support, chunked encoding only
  • Multiple I/O implementations (Java NIO and Linux IO-Uring)
  • ZIO2 native implementation
  • High-performance packet streaming
  • HTTP multipart support with ZStream interface
  • Automatic file saving for multipart uploads
  • Web filter support
  • Configurable logging
  • Reactive Flow Control: Advanced backpressure mechanisms that integrate with ZStream
  • End-to-End Backpressure: Complete backpressure chain from network socket to application logic
  • Threshold-Based Window Updates: Optimized HTTP/2 flow control with intelligent window management

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%

About

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).

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published