Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
da4da1b
add process for conformance suite
Saturn225 Sep 26, 2024
969afa9
workflow for conformance suite added
Saturn225 Sep 26, 2024
698dd7c
fix(conformance): forbidden duplicate headers
Saturn225 Sep 30, 2024
bb47958
fix(conformance): tests and move to other suite for e2e validation
Saturn225 Sep 30, 2024
453c609
chore: cleanup and fix 404 and 405 tests
Saturn225 Sep 30, 2024
73a209b
Update ServerInboundHandler.scala
Saturn225 Sep 30, 2024
1e66f64
Update Routes.scala
Saturn225 Oct 1, 2024
1948dc4
feat(conformance): 404
Saturn225 Oct 1, 2024
6514ff7
chore(conformance): cleanup netty exception tests
Saturn225 Oct 1, 2024
943167c
fix(conformance): fmt
Saturn225 Oct 1, 2024
5257f23
remove netty exceptions added
Saturn225 Oct 1, 2024
b609cd9
fix
Saturn225 Oct 1, 2024
62e78a3
feat(conformance): add review comments
Saturn225 Oct 2, 2024
70d026e
Merge branch 'test-1' into feat/conformance-spec
Saturn225 Oct 10, 2024
f2ccf4a
Handle OPTIONS Method
Saturn225 Oct 19, 2024
66dbe0d
fix in TestServer for validation
Saturn225 Oct 19, 2024
864d676
remove dev logs
Saturn225 Oct 19, 2024
6a29249
Merge branch 'main' into feat/conformance-spec
Saturn225 Oct 19, 2024
957b35f
fix: conformance tests
Saturn225 Oct 26, 2024
265e7e6
Merge branch 'main' into feat/conformance-spec
987Nabil Oct 29, 2024
9b0638e
Merge branch 'main' into feat/conformance-spec
Saturn225 Nov 6, 2024
ab4f1fd
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 9, 2024
e165ef1
Relax Host header validation logic to allow broader compatibility
Saturn225 Dec 11, 2024
078c76b
Merge branch 'main' into feat/conformance-spec
987Nabil Dec 14, 2024
769e451
update build pipeline for conformance tests
Saturn225 Dec 14, 2024
ecd6797
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 14, 2024
bcca045
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 19, 2024
a19b7e6
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 25, 2024
58910d9
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 26, 2024
1725131
Merge branch 'main' into feat/conformance-spec
Saturn225 Dec 28, 2024
7f74dbb
Merge branch 'main' into feat/conformance-spec
Saturn225 Jan 18, 2025
19b88e9
Merge branch 'main' into feat/conformance-spec
Saturn225 Jan 22, 2025
87194e9
Merge branch 'main' into feat/conformance-spec
Saturn225 Jan 23, 2025
8a50242
mark as private
Saturn225 Jan 23, 2025
0e0f746
feat: add review suggestions
Saturn225 Jan 23, 2025
a9dd62f
cleanup
Saturn225 Jan 23, 2025
fb1db92
fmt
Saturn225 Jan 23, 2025
4b2cd8a
ignore test for missing header to add 401 Unauthorized
Saturn225 Jan 23, 2025
591d7e5
fmt
Saturn225 Jan 23, 2025
8f510fe
Trigger Build
Saturn225 Jan 23, 2025
c8cc909
Merge branch 'main' into feat/conformance-spec
987Nabil Feb 18, 2025
12a4b8f
Merge branch 'main' into feat/conformance-spec
Saturn225 Mar 18, 2025
eff361b
cleanup
Saturn225 Mar 18, 2025
fdd1657
provide scope
Saturn225 Mar 18, 2025
5623ceb
fix build
Saturn225 Mar 18, 2025
8361979
Merge branch 'main' into feat/conformance-spec
Saturn225 Mar 30, 2025
0b8abb5
Merge branch 'main' into feat/conformance-spec
Saturn225 Apr 4, 2025
a532d1f
Merge branch 'main' into feat/conformance-spec
Saturn225 Apr 15, 2025
91e58b4
Merge branch 'main' into feat/conformance-spec
Saturn225 Apr 20, 2025
0d11203
re-trigger build
Saturn225 Apr 20, 2025
1743b79
Merge branch 'main' into feat/conformance-spec
Saturn225 May 7, 2025
f5e97bb
Merge branch 'zio:main' into feat/conformance-spec
Saturn225 May 9, 2025
f5f7eb2
feat(server): add `validateHeaders` config flag to control built-in a…
Saturn225 May 9, 2025
6256e79
make mima happier
Saturn225 May 25, 2025
72b31c3
Merge branch 'main' into feat/conformance-spec
Saturn225 May 25, 2025
c116e86
Merge branch 'main' into feat/conformance-spec
Saturn225 May 26, 2025
cf3a4d8
Merge branch 'main' into feat/conformance-spec
Saturn225 May 31, 2025
23f4eeb
fix: fmt
Saturn225 May 31, 2025
753ad19
Merge branch 'main' into feat/conformance-spec
Saturn225 Jun 2, 2025
6573ae7
Merge branch 'main' into feat/conformance-spec
Saturn225 Jun 4, 2025
36abd38
Merge branch 'main' into feat/conformance-spec
Saturn225 Jun 10, 2025
bc4be4a
Merge branch 'main' into feat/conformance-spec
Saturn225 Jun 13, 2025
f18f58d
Merge branch 'main' into feat/conformance-spec
Saturn225 Jun 17, 2025
598cf84
Merge branch 'main' into feat/conformance-spec
987Nabil Jun 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/http-conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: HTTP Conformance

on:
pull_request:
branches: ["**"]
push:
branches: ["**"]
tags: [v*]

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JDK_JAVA_OPTIONS: "-Xms4G -Xmx8G -XX:+UseG1GC -Xss10M -XX:ReservedCodeCacheSize=1G -XX:NonProfiledCodeHeapSize=512m -Dfile.encoding=UTF-8"
SBT_OPTS: "-Xms4G -Xmx8G -XX:+UseG1GC -Xss10M -XX:ReservedCodeCacheSize=1G -XX:NonProfiledCodeHeapSize=512m -Dfile.encoding=UTF-8"

jobs:
build:
name: Build and Test
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.12.19, 2.13.14, 3.3.3]
java:
- graal_graalvm@17
- graal_graalvm@21
- temurin@17
- temurin@21
runs-on: ${{ matrix.os }}
timeout-minutes: 60

steps:
- name: Checkout current branch (full)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup GraalVM (graal_graalvm@17)
if: matrix.java == 'graal_graalvm@17'
uses: graalvm/setup-graalvm@v1
with:
java-version: 17
distribution: graalvm
components: native-image
github-token: ${{ secrets.GITHUB_TOKEN }}
cache: sbt

- uses: coursier/setup-action@v1
with:
apps: sbt

- name: Setup GraalVM (graal_graalvm@21)
if: matrix.java == 'graal_graalvm@21'
uses: graalvm/setup-graalvm@v1
with:
java-version: 21
distribution: graalvm
components: native-image
github-token: ${{ secrets.GITHUB_TOKEN }}
cache: sbt

- name: Setup Java (temurin@17)
if: matrix.java == 'temurin@17'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: sbt

- name: Setup Java (temurin@21)
if: matrix.java == 'temurin@21'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: sbt

- name: Run HTTP Conformance Tests
run: sbt "project zioHttpJVM" "testOnly zio.http.ConformanceSpec zio.http.ConformanceE2ESpec"
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ object HelloWorldAdvanced extends ZIOAppDefault {
// Configure thread count using CLI
val nThreads: Int = args.headOption.flatMap(x => Try(x.toInt).toOption).getOrElse(0)

val config = Server.Config.default
val config = Server.Config.default
.port(PORT)
val nettyConfig = NettyConfig.default
val nettyConfig = NettyConfig.default
.leakDetection(LeakDetectionLevel.PARANOID)
.maxThreads(nThreads)
val configLayer = ZLayer.succeed(config)
val nettyConfigLayer = ZLayer.succeed(nettyConfig)
val configLayer = ZLayer.succeed(config)
val nettyConfigLayer = ZLayer.succeed(nettyConfig)
val serverRuntimeConfig = configLayer.flatMap(env => ZLayer.succeed(ServerRuntimeConfig(env.get)))

(fooBar ++ app)
.serve[Any]
.provide(configLayer, nettyConfigLayer, Server.customized)
.provide(serverRuntimeConfig, nettyConfigLayer, Server.customized)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ object PlainTextBenchmarkServer extends ZIOAppDefault {
private val nettyConfig = NettyConfig.default
.leakDetection(LeakDetectionLevel.DISABLED)

private val configLayer = ZLayer.succeed(config)
private val nettyConfigLayer = ZLayer.succeed(nettyConfig)
private val configLayer = ZLayer.succeed(config)
private val nettyConfigLayer = ZLayer.succeed(nettyConfig)
private val serverRuntimeConfigLayer = configLayer.flatMap(env => ZLayer.succeed(ServerRuntimeConfig(env.get)))

val run: UIO[ExitCode] =
Server.serve(routes).provide(configLayer, nettyConfigLayer, Server.customized).exitCode
Server.serve(routes).provide(serverRuntimeConfigLayer, nettyConfigLayer, Server.customized).exitCode

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ object SimpleEffectBenchmarkServer extends ZIOAppDefault {
private val nettyConfig = NettyConfig.default
.leakDetection(LeakDetectionLevel.DISABLED)

private val configLayer = ZLayer.succeed(config)
private val nettyConfigLayer = ZLayer.succeed(nettyConfig)
private val configLayer = ZLayer.succeed(config)
private val nettyConfigLayer = ZLayer.succeed(nettyConfig)
private val serverRuntimeConfigLayer = configLayer.flatMap(env => ZLayer.succeed(ServerRuntimeConfig(env.get)))

val run: UIO[ExitCode] =
Server.serve(routes).provide(configLayer, nettyConfigLayer, Server.customized).exitCode
Server.serve(routes).provide(serverRuntimeConfigLayer, nettyConfigLayer, Server.customized).exitCode

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ object TestServer {
val default: ZLayer[Any, Nothing, TestServer] = ZLayer.make[TestServer][Nothing](
TestServer.layer.orDie,
ZLayer.succeed(Server.Config.default.onAnyOpenPort),
ServerRuntimeConfig.layer,
NettyDriver.customized.orDie,
ZLayer.succeed(NettyConfig.defaultWithFastShutdown),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ object RoutesPrecedentsSpec extends ZIOSpecDefault {
ZLayer.succeed(new MyServiceLive(code)),
)
}.provide(
ServerRuntimeConfig.layer,
ZLayer.succeed(Server.Config.default.onAnyOpenPort),
TestServer.layer,
Client.default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ object SocketContractSpec extends ZIOHttpSpec {
_ <- promise.await.timeout(10.seconds)
} yield assert(response.status)(equalTo(Status.SwitchingProtocols))
}.provideSome[Client](
ServerRuntimeConfig.layer,
TestServer.layer,
NettyDriver.customized,
ZLayer.succeed(NettyConfig.defaultWithFastShutdown),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ object TestServerSpec extends ZIOHttpSpec {
port <- ZIO.serviceWithZIO[Server](_.port)
} yield Request
.get(url = URL.root.port(port))
.addHeaders(Headers(Header.Accept(MediaType.text.`plain`)))

.addHeaders(
Headers(
Header.Accept(MediaType.text.`plain`),
Header.Host("localhost"),
),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ trait ServerPlatformSpecific {

private[http] def base: ZLayer[Driver & Config, Throwable, Server]

val customized: ZLayer[Config & NettyConfig, Throwable, Driver with Server] = {
val customized: ZLayer[ServerRuntimeConfig & NettyConfig, Throwable, Driver with Server] = {
// tmp val Needed for Scala2
val tmp: ZLayer[Driver & Config, Throwable, Server] = ZLayer.suspend(base)

ZLayer.makeSome[Config & NettyConfig, Driver with Server](
ZLayer.makeSome[ServerRuntimeConfig & NettyConfig, Driver with Server](
ZLayer.fromFunction((runtime: ServerRuntimeConfig) => runtime.config),
NettyDriver.customized,
tmp,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ import java.net.InetSocketAddress
import zio._

import zio.http.Driver.StartResult
import zio.http._
import zio.http.netty._
import zio.http.netty.client.NettyClientDriver
import zio.http.{ClientDriver, Driver, Response, Routes, Server}

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel._
import io.netty.channel.{Channel => NettyChannel, ChannelFactory, ChannelInitializer, ChannelOption, ServerChannel}
import io.netty.util.ResourceLeakDetector

private[zio] final case class NettyDriver(
appRef: RoutesRef,
channelFactory: ChannelFactory[ServerChannel],
channelInitializer: ChannelInitializer[Channel],
channelInitializer: ChannelInitializer[NettyChannel],
serverInboundHandler: ServerInboundHandler,
eventLoopGroups: ServerEventLoopGroups,
serverConfig: Server.Config,
serverConfig: ServerRuntimeConfig,
nettyConfig: NettyConfig,
) extends Driver { self =>

Expand All @@ -47,9 +47,9 @@ private[zio] final case class NettyDriver(
.group(eventLoopGroups.boss, eventLoopGroups.worker)
.channelFactory(channelFactory)
.childHandler(channelInitializer)
.option[Integer](ChannelOption.SO_BACKLOG, serverConfig.soBacklog)
.childOption[JBoolean](ChannelOption.TCP_NODELAY, serverConfig.tcpNoDelay)
.bind(serverConfig.address)
.option[Integer](ChannelOption.SO_BACKLOG, serverConfig.config.soBacklog)
.childOption[JBoolean](ChannelOption.TCP_NODELAY, serverConfig.config.tcpNoDelay)
.bind(serverConfig.config.address)
}
_ <- NettyFutureExecutor.scoped(chf)
_ <- ZIO.succeed(ResourceLeakDetector.setLevel(nettyConfig.leakDetectionLevel.toNetty))
Expand Down Expand Up @@ -96,9 +96,9 @@ object NettyDriver {
val make: ZIO[
RoutesRef
& ChannelFactory[ServerChannel]
& ChannelInitializer[Channel]
& ChannelInitializer[NettyChannel]
& ServerEventLoopGroups
& Server.Config
& ServerRuntimeConfig
& NettyConfig
& ServerInboundHandler,
Nothing,
Expand All @@ -107,9 +107,9 @@ object NettyDriver {
for {
app <- ZIO.service[RoutesRef]
cf <- ZIO.service[ChannelFactory[ServerChannel]]
cInit <- ZIO.service[ChannelInitializer[Channel]]
cInit <- ZIO.service[ChannelInitializer[NettyChannel]]
elg <- ZIO.service[ServerEventLoopGroups]
sc <- ZIO.service[Server.Config]
sc <- ZIO.service[ServerRuntimeConfig]
nsc <- ZIO.service[NettyConfig]
sih <- ZIO.service[ServerInboundHandler]
} yield new NettyDriver(
Expand All @@ -122,23 +122,24 @@ object NettyDriver {
nettyConfig = nsc,
)

val manual
: ZLayer[ServerEventLoopGroups & ChannelFactory[ServerChannel] & Server.Config & NettyConfig, Nothing, Driver] = {
val manual: ZLayer[ServerEventLoopGroups & ChannelFactory[
ServerChannel,
] & ServerRuntimeConfig & NettyConfig, Nothing, Driver] = {
implicit val trace: Trace = Trace.empty
ZLayer.makeSome[ServerEventLoopGroups & ChannelFactory[ServerChannel] & Server.Config & NettyConfig, Driver](
ZLayer.makeSome[ServerEventLoopGroups & ChannelFactory[ServerChannel] & ServerRuntimeConfig & NettyConfig, Driver](
ZLayer(AppRef.empty),
ServerChannelInitializer.layer,
ServerInboundHandler.live,
ZLayer(make),
)
}

val customized: ZLayer[Server.Config & NettyConfig, Throwable, Driver] = {
val customized: ZLayer[ServerRuntimeConfig & NettyConfig, Throwable, Driver] = {
val serverChannelFactory: ZLayer[NettyConfig, Nothing, ChannelFactory[ServerChannel]] =
ChannelFactories.Server.fromConfig
val eventLoopGroup: ZLayer[NettyConfig, Nothing, ServerEventLoopGroups] = ServerEventLoopGroups.live

ZLayer.makeSome[Server.Config & NettyConfig, Driver](
ZLayer.makeSome[ServerRuntimeConfig & NettyConfig, Driver](
eventLoopGroup,
serverChannelFactory,
manual,
Expand All @@ -148,6 +149,7 @@ object NettyDriver {
val live: ZLayer[Server.Config, Throwable, Driver] =
ZLayer.makeSome[Server.Config, Driver](
ZLayer.succeed(NettyConfig.default),
ServerRuntimeConfig.layer,
customized,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import java.util.concurrent.TimeUnit
import zio._
import zio.stacktracer.TracingImplicits.disableAutoTrace

import zio.http.Server
import zio.http.Server.RequestStreaming
import zio.http.netty.model.Conversions
import zio.http.netty.{HybridContentLengthHandler, Names}
import zio.http.{Server, ServerRuntimeConfig}

import io.netty.channel.ChannelHandler.Sharable
import io.netty.channel._
Expand All @@ -38,7 +38,7 @@ import io.netty.handler.timeout.ReadTimeoutHandler
*/
@Sharable
private[zio] final case class ServerChannelInitializer(
cfg: Server.Config,
cfg: ServerRuntimeConfig,
reqHandler: ChannelInboundHandler,
) extends ChannelInitializer[Channel] {

Expand All @@ -48,11 +48,11 @@ private[zio] final case class ServerChannelInitializer(
val pipeline = channel.pipeline()
// SSL
// Add SSL Handler if CTX is available
cfg.sslConfig.foreach { sslCfg =>
pipeline.addFirst(Names.SSLHandler, new ServerSSLDecoder(sslCfg, cfg))
cfg.config.sslConfig.foreach { sslCfg =>
pipeline.addFirst(Names.SSLHandler, new ServerSSLDecoder(sslCfg, cfg.config))
}

cfg.idleTimeout.foreach { timeout =>
cfg.config.idleTimeout.foreach { timeout =>
pipeline.addLast(Names.ReadTimeoutHandler, new ReadTimeoutHandler(timeout.toMillis, TimeUnit.MILLISECONDS))
}

Expand All @@ -62,27 +62,29 @@ private[zio] final case class ServerChannelInitializer(
Names.HttpRequestDecoder,
new HttpRequestDecoder(
new HttpDecoderConfig()
.setMaxInitialLineLength(cfg.maxInitialLineLength)
.setMaxHeaderSize(cfg.maxHeaderSize)
.setMaxInitialLineLength(cfg.config.maxInitialLineLength)
.setMaxHeaderSize(cfg.config.maxHeaderSize)
.setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE)
.setValidateHeaders(false),
.setValidateHeaders(cfg.validateHeaders),
),
)
pipeline.addLast(Names.HttpResponseEncoder, new HttpResponseEncoder())

// HttpContentDecompressor
if (cfg.requestDecompression.enabled)
pipeline.addLast(Names.HttpRequestDecompression, new HttpContentDecompressor(cfg.requestDecompression.strict, 0))

cfg.responseCompression.foreach(ops => {
if (cfg.config.requestDecompression.enabled)
pipeline.addLast(
Names.HttpRequestDecompression,
new HttpContentDecompressor(cfg.config.requestDecompression.strict, 0),
)
cfg.config.responseCompression.foreach(ops => {
pipeline.addLast(
Names.HttpResponseCompression,
new HttpContentCompressor(ops.contentThreshold, ops.options.map(Conversions.compressionOptionsToNetty): _*),
)
})

// ObjectAggregator
cfg.requestStreaming match {
cfg.config.requestStreaming match {
case RequestStreaming.Enabled =>
case RequestStreaming.Disabled(maximumContentLength) =>
pipeline.addLast(Names.HttpObjectAggregator, new HttpObjectAggregator(maximumContentLength))
Expand All @@ -93,11 +95,12 @@ private[zio] final case class ServerChannelInitializer(

// ExpectContinueHandler
// Add expect continue handler is settings is true
if (cfg.acceptContinue) pipeline.addLast(Names.HttpServerExpectContinue, new HttpServerExpectContinueHandler())
if (cfg.config.acceptContinue)
pipeline.addLast(Names.HttpServerExpectContinue, new HttpServerExpectContinueHandler())

// KeepAliveHandler
// Add Keep-Alive handler is settings is true
if (cfg.keepAlive) pipeline.addLast(Names.HttpKeepAliveHandler, new HttpServerKeepAliveHandler)
if (cfg.config.keepAlive) pipeline.addLast(Names.HttpKeepAliveHandler, new HttpServerKeepAliveHandler)

pipeline.addLast(Names.HttpServerFlushConsolidation, new FlushConsolidationHandler())

Expand All @@ -112,10 +115,11 @@ private[zio] final case class ServerChannelInitializer(
object ServerChannelInitializer {
implicit val trace: Trace = Trace.empty

val layer: ZLayer[SimpleChannelInboundHandler[HttpObject] with Server.Config, Nothing, ServerChannelInitializer] =
val layer
: ZLayer[SimpleChannelInboundHandler[HttpObject] with ServerRuntimeConfig, Nothing, ServerChannelInitializer] =
ZLayer.fromZIO {
for {
cfg <- ZIO.service[Server.Config]
cfg <- ZIO.service[ServerRuntimeConfig]
handler <- ZIO.service[SimpleChannelInboundHandler[HttpObject]]
} yield ServerChannelInitializer(cfg, handler)
}
Expand Down
Loading
Loading