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

Add maxMessagePerRead setting for server and child channels #439

Closed
wants to merge 3 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
37 changes: 19 additions & 18 deletions Sources/Hummingbird/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public struct ApplicationConfiguration: Sendable {
public var serverName: String?
/// Defines the maximum length for the queue of pending connections
public var backlog: Int
/// This will affect how many connections the server accepts at any one time
public var serverMaxMessagesPerRead: UInt?
/// This will affect how much is read from a connection at any one time
public var childMaxMessagesPerRead: UInt?
/// Allows socket to be bound to an address that is already in use.
public var reuseAddress: Bool
#if canImport(Network)
Expand All @@ -47,16 +51,24 @@ public struct ApplicationConfiguration: Sendable {
/// - serverName: Server name to return in "server" header
/// - backlog: the maximum length for the queue of pending connections. If a connection request arrives with the queue full,
/// the client may receive an error with an indication of ECONNREFUSE
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
/// - serverMaxMessagesPerRead: This will affect how many connections the server accepts before waiting for notification of
/// more. Setting this too high can flood the server with too much work. DO NOT EDIT this unless you know what you are doing
/// - childMaxMessagesPerRead: This will affect how much is read from a connection before waiting for notification of more. DO
/// NOT EDIT this unless you know what you are doing
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
public init(
address: Address = .hostname(),
serverName: String? = nil,
backlog: Int = 256,
serverMaxMessagesPerRead: UInt? = nil,
childMaxMessagesPerRead: UInt? = nil,
reuseAddress: Bool = true
) {
self.address = address
self.serverName = serverName
self.backlog = backlog
self.serverMaxMessagesPerRead = serverMaxMessagesPerRead
self.childMaxMessagesPerRead = childMaxMessagesPerRead
self.reuseAddress = reuseAddress
#if canImport(Network)
self.tlsOptions = .none
Expand All @@ -79,35 +91,22 @@ public struct ApplicationConfiguration: Sendable {
) {
self.address = address
self.serverName = serverName
self.backlog = 256 // not used by Network framework
self.reuseAddress = reuseAddress
self.tlsOptions = tlsOptions
// The following are not used by Network framework
self.backlog = 256
self.serverMaxMessagesPerRead = nil
self.childMaxMessagesPerRead = nil
}

#endif

/// Create new configuration struct with updated values
public func with(
address: Address? = nil,
serverName: String? = nil,
backlog: Int? = nil,
reuseAddress: Bool? = nil
) -> Self {
return .init(
address: address ?? self.address,
serverName: serverName ?? self.serverName,
backlog: backlog ?? self.backlog,
reuseAddress: reuseAddress ?? self.reuseAddress
)
}

/// return HTTP server configuration
#if canImport(Network)
var httpServer: ServerConfiguration {
return .init(
address: self.address,
serverName: self.serverName,
backlog: self.backlog,
reuseAddress: self.reuseAddress,
tlsOptions: self.tlsOptions
)
Expand All @@ -118,6 +117,8 @@ public struct ApplicationConfiguration: Sendable {
address: self.address,
serverName: self.serverName,
backlog: self.backlog,
serverMaxMessagesPerRead: self.serverMaxMessagesPerRead,
childMaxMessagesPerRead: self.childMaxMessagesPerRead,
reuseAddress: self.reuseAddress
)
}
Expand Down
10 changes: 8 additions & 2 deletions Sources/HummingbirdCore/Server/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,19 @@ public actor Server<ChildChannel: ServerChildChannel>: Service {
private nonisolated func createSocketsBootstrap(
configuration: ServerConfiguration
) -> ServerBootstrap {
return ServerBootstrap(group: self.eventLoopGroup)
var bootstrap = ServerBootstrap(group: self.eventLoopGroup)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: numericCast(configuration.backlog))
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: configuration.reuseAddress ? 1 : 0)
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: configuration.reuseAddress ? 1 : 0)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 1)
.childChannelOption(ChannelOptions.allowRemoteHalfClosure, value: true)
if let serverMaxMessagesPerRead = configuration.serverMaxMessagesPerRead {
bootstrap = bootstrap.serverChannelOption(ChannelOptions.maxMessagesPerRead, value: serverMaxMessagesPerRead)
}
if let childMaxMessagesPerRead = configuration.childMaxMessagesPerRead {
bootstrap = bootstrap.serverChannelOption(ChannelOptions.maxMessagesPerRead, value: childMaxMessagesPerRead)
}
return bootstrap
}

#if canImport(Network)
Expand Down
32 changes: 22 additions & 10 deletions Sources/HummingbirdCore/Server/ServerConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@ import NIOCore
/// HTTP server configuration
public struct ServerConfiguration: Sendable {
/// Bind address for server
public let address: Address
public var address: Address
/// Server name to return in "server" header
public let serverName: String?
public var serverName: String?
/// Defines the maximum length for the queue of pending connections
public let backlog: Int
public var backlog: Int
/// This will affect how many connections the server accepts at any one time
public var serverMaxMessagesPerRead: UInt?
/// This will affect how much is read from a connection at any one time
public var childMaxMessagesPerRead: UInt?
/// Allows socket to be bound to an address that is already in use.
public let reuseAddress: Bool
public var reuseAddress: Bool
#if canImport(Network)
/// TLS options for NIO Transport services
public let tlsOptions: TSTLSOptions
public var tlsOptions: TSTLSOptions
#endif

/// Initialize server configuration
Expand All @@ -35,16 +39,24 @@ public struct ServerConfiguration: Sendable {
/// - serverName: Server name to return in "server" header
/// - backlog: the maximum length for the queue of pending connections. If a connection request arrives with the queue full,
/// the client may receive an error with an indication of ECONNREFUSE
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
/// - serverMaxMessagesPerRead: This will affect how many connections the server accepts before waiting for notification of
/// more. Setting this too high can flood the server with too much work. DO NOT EDIT this unless you know what you are doing
/// - childMaxMessagesPerRead: This will affect how much is read from a connection before waiting for notification of more. DO NOT
/// EDIT this unless you know what you are doing
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
public init(
address: Address = .hostname(),
serverName: String? = nil,
backlog: Int = 256,
serverMaxMessagesPerRead: UInt? = nil,
childMaxMessagesPerRead: UInt? = nil,
reuseAddress: Bool = true
) {
self.address = address
self.serverName = serverName
self.backlog = backlog
self.serverMaxMessagesPerRead = serverMaxMessagesPerRead
self.childMaxMessagesPerRead = childMaxMessagesPerRead
self.reuseAddress = reuseAddress
#if canImport(Network)
self.tlsOptions = .none
Expand All @@ -55,23 +67,23 @@ public struct ServerConfiguration: Sendable {
/// - Parameters:
/// - address: Bind address for server
/// - serverName: Server name to return in "server" header
/// - backlog: the maximum length for the queue of pending connections. If a connection request arrives with the queue full,
/// the client may receive an error with an indication of ECONNREFUSE
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
/// - tlsOptions: TLS options for when you are using NIOTransportServices
#if canImport(Network)
public init(
address: Address = .hostname(),
serverName: String? = nil,
backlog: Int = 256,
reuseAddress: Bool = true,
tlsOptions: TSTLSOptions
) {
self.address = address
self.serverName = serverName
self.backlog = backlog
self.reuseAddress = reuseAddress
self.tlsOptions = tlsOptions
// The following are unsupported by transport services
self.backlog = 256
self.serverMaxMessagesPerRead = nil
self.childMaxMessagesPerRead = nil
}
#endif
}
7 changes: 6 additions & 1 deletion Sources/HummingbirdTesting/TestApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ internal struct TestApplication<BaseApp: ApplicationProtocol>: ApplicationProtoc
/// Event loop group used by application
var eventLoopGroup: EventLoopGroup { self.base.eventLoopGroup }
/// Configuration
var configuration: ApplicationConfiguration { self.base.configuration.with(address: .hostname("localhost", port: 0)) }
var configuration: ApplicationConfiguration {
var configuration = self.base.configuration
configuration.address = .hostname("localhost", port: 0)
return configuration
}

/// Logger
var logger: Logger { self.base.logger }
/// On server running
Expand Down
Loading