Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying maxIninitialLineLength and maxChunkSize #2522

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 29 additions & 1 deletion zio-http/src/main/scala/zio/http/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ import zio.http.Server.Config.ResponseCompressionConfig
import zio.http.netty.NettyConfig
import zio.http.netty.server._

import io.netty.handler.codec.http.HttpObjectDecoder.{
DEFAULT_MAX_CHUNK_SIZE,
DEFAULT_MAX_HEADER_SIZE,
DEFAULT_MAX_INITIAL_LINE_LENGTH,
}

/**
* Represents a server, which is capable of serving zero or more HTTP
* applications.
Expand Down Expand Up @@ -54,7 +60,9 @@ object Server {
requestDecompression: Decompression,
responseCompression: Option[ResponseCompressionConfig],
requestStreaming: RequestStreaming,
maxInitialLineLength: Int,
maxHeaderSize: Int,
maxChunkSize: Int,
logWarningOnFatalError: Boolean,
gracefulShutdownTimeout: Duration,
webSocketConfig: WebSocketConfig,
Expand Down Expand Up @@ -112,12 +120,24 @@ object Server {
*/
def logWarningOnFatalError(enable: Boolean): Config = self.copy(logWarningOnFatalError = enable)

/**
* Configure the server to use `maxInitialLineLength` value when
* encode/decode headers.
*/
def maxInitialLineLength(lineLength: Int): Config = self.copy(maxInitialLineLength = lineLength)

/**
* Configure the server to use `maxHeaderSize` value when encode/decode
* headers.
*/
def maxHeaderSize(headerSize: Int): Config = self.copy(maxHeaderSize = headerSize)

/**
* Configure the server to use `maxChunkSize` value when encode/decode
* headers.
*/
def maxChunkSize(chunkSize: Int): Config = self.copy(maxChunkSize = chunkSize)

def noIdleTimeout: Config = self.copy(idleTimeout = None)

/**
Expand Down Expand Up @@ -169,7 +189,9 @@ object Server {
Decompression.config.nested("request-decompression").withDefault(Config.default.requestDecompression) ++
ResponseCompressionConfig.config.nested("response-compression").optional ++
RequestStreaming.config.nested("request-streaming").withDefault(Config.default.requestStreaming) ++
zio.Config.int("max-initial-line-length").withDefault(Config.default.maxInitialLineLength) ++
zio.Config.int("max-header-size").withDefault(Config.default.maxHeaderSize) ++
zio.Config.int("max-chunk-size").withDefault(Config.default.maxChunkSize) ++
zio.Config.boolean("log-warning-on-fatal-error").withDefault(Config.default.logWarningOnFatalError) ++
zio.Config.duration("graceful-shutdown-timeout").withDefault(Config.default.gracefulShutdownTimeout) ++
zio.Config.duration("idle-timeout").optional.withDefault(Config.default.idleTimeout)
Expand All @@ -183,7 +205,9 @@ object Server {
requestDecompression,
responseCompression,
requestStreaming,
maxInitialLineLength,
maxHeaderSize,
maxChunkSize,
logWarningOnFatalError,
gracefulShutdownTimeout,
idleTimeout,
Expand All @@ -196,7 +220,9 @@ object Server {
requestDecompression = requestDecompression,
responseCompression = responseCompression,
requestStreaming = requestStreaming,
maxInitialLineLength = maxInitialLineLength,
maxHeaderSize = maxHeaderSize,
maxChunkSize = maxChunkSize,
logWarningOnFatalError = logWarningOnFatalError,
gracefulShutdownTimeout = gracefulShutdownTimeout,
idleTimeout = idleTimeout,
Expand All @@ -211,7 +237,9 @@ object Server {
requestDecompression = Decompression.No,
responseCompression = None,
requestStreaming = RequestStreaming.Disabled(1024 * 100),
maxHeaderSize = 8192,
maxInitialLineLength = DEFAULT_MAX_INITIAL_LINE_LENGTH,
maxHeaderSize = DEFAULT_MAX_HEADER_SIZE,
maxChunkSize = DEFAULT_MAX_CHUNK_SIZE,
logWarningOnFatalError = true,
gracefulShutdownTimeout = 10.seconds,
webSocketConfig = WebSocketConfig.default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import zio.http.netty.model.Conversions

import io.netty.channel.ChannelHandler.Sharable
import io.netty.channel._
import io.netty.handler.codec.http.HttpObjectDecoder.{DEFAULT_MAX_CHUNK_SIZE, DEFAULT_MAX_INITIAL_LINE_LENGTH}
import io.netty.handler.codec.http._
import io.netty.handler.flush.FlushConsolidationHandler
import io.netty.handler.timeout.ReadTimeoutHandler
Expand Down Expand Up @@ -60,7 +59,7 @@ private[zio] final case class ServerChannelInitializer(
// Instead of ServerCodec, we should use Decoder and Encoder separately to have more granular control over performance.
pipeline.addLast(
Names.HttpRequestDecoder,
new HttpRequestDecoder(DEFAULT_MAX_INITIAL_LINE_LENGTH, cfg.maxHeaderSize, DEFAULT_MAX_CHUNK_SIZE, false),
new HttpRequestDecoder(cfg.maxInitialLineLength, cfg.maxHeaderSize, cfg.maxChunkSize, false),
)
pipeline.addLast(Names.HttpResponseEncoder, new HttpResponseEncoder())

Expand Down
19 changes: 16 additions & 3 deletions zio-http/src/test/scala/zio/http/ServerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ object ServerSpec extends HttpRunnableSpec {
content <- HttpGen.nonEmptyBody(Gen.const(data))
} yield (data.mkString(""), content)

private val port = 8080
private val MaxSize = 1024 * 10
val configApp = Server.Config.default
private val port = 8080
private val MaxSize = 1024 * 10
private val MaxPathLength = 6144

val configApp = Server.Config.default
.requestDecompression(true)
.disableRequestStreaming(MaxSize)
.port(port)
.responseCompression()
.maxInitialLineLength(MaxPathLength * 2)

private val app = serve

Expand Down Expand Up @@ -297,6 +300,16 @@ object ServerSpec extends HttpRunnableSpec {
.toHttpApp
val res = app.deploy.status.run(method = Method.POST, body = Body.fromString("some text"))
assertZIO(res)(equalTo(Status.Ok))
} + test("can support long urls") {
check(Gen.alphaNumericStringBounded(0, MaxPathLength)) { path =>
val app = Routes
.singleton(handler { (_: Path, req: Request) => req.body.asChunk.as(Response.ok) })
.sandbox
.toHttpApp
val res =
app.deploy.status.run(method = Method.POST, path = Path.root / path, body = Body.fromString("some text"))
assertZIO(res)(equalTo(Status.Ok))
}
}
}

Expand Down
Loading