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

[WIP] feat: Ability to limit request size and connection count using HTTPServerConfigurationHandler. #222

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
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0"),
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.0.0"),
.package(url: "https://github.com/IBM-Swift/BlueSSLService.git", from: "1.0.0"),
.package(url: "https://github.com/IBM-Swift/LoggerAPI.git", from: "1.7.3")
.package(url: "https://github.com/IBM-Swift/LoggerAPI.git", from: "1.7.3"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand All @@ -42,7 +42,7 @@ let package = Package(
dependencies: []),
.target(
name: "KituraNet",
dependencies: ["NIO", "NIOFoundationCompat", "NIOHTTP1", "NIOSSL", "SSLService", "LoggerAPI", "NIOWebSocket", "CLinuxHelpers", "NIOExtras"]),
dependencies: ["NIO", "NIOFoundationCompat", "NIOHTTP1", "NIOSSL", "SSLService", "LoggerAPI", "NIOWebSocket", "CLinuxHelpers", "NIOExtras", "NIOConcurrencyHelpers"]),
.testTarget(
name: "KituraNetTests",
dependencies: ["KituraNet"])
Expand Down
5 changes: 3 additions & 2 deletions Sources/KituraNet/HTTP/HTTP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ public class HTTP {
let server = HTTP.createServer()
````
*/
public static func createServer() -> HTTPServer {
return HTTPServer()
public static func createServer(serverConfig: HTTPServerConfiguration = .default) -> HTTPServer {
let serverConfig = serverConfig
return HTTPServer(serverConfig: serverConfig)
}

/**
Expand Down
27 changes: 22 additions & 5 deletions Sources/KituraNet/HTTP/HTTPRequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import Foundation
import Dispatch

internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandler {

/// The HTTPServer instance on which this handler is installed
var server: HTTPServer

Expand Down Expand Up @@ -79,6 +78,14 @@ internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandle

switch request {
case .head(let header):
if let requestSizeLimit = server.serverConfig.requestSizeLimit,
let contentLength = header.headers["Content-Length"].first,
let contentLengthValue = Int(contentLength) {
if contentLengthValue > requestSizeLimit {
sendRequestTooLongResponse(context: context)
context.close()
}
}
serverRequest = HTTPServerRequest(channel: context.channel, requestHead: header, enableSSL: enableSSLVerification)
self.clientRequestedKeepAlive = header.isKeepAlive
case .body(var buffer):
Expand All @@ -92,6 +99,7 @@ internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandle
serverRequest.buffer!.byteBuffer.writeBuffer(&buffer)
}
case .end:
let resetRequestSize = 0
serverResponse = HTTPServerResponse(channel: context.channel, handler: self)
//Make sure we use the latest delegate registered with the server
DispatchQueue.global().async {
Expand All @@ -100,14 +108,12 @@ internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandle
Monitor.delegate?.started(request: serverRequest, response: serverResponse)
delegate.handle(request: serverRequest, response: serverResponse)
}
self.userInboundEventTriggered(context: context, event: resetRequestSize)
}
}

//IdleStateEvents are received on this method
public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
if event is IdleStateHandler.IdleStateEvent {
_ = context.close()
}
context.triggerUserOutboundEvent(event, promise: nil)
}

public func channelReadComplete(context: ChannelHandlerContext) {
Expand Down Expand Up @@ -152,4 +158,15 @@ internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandle
func updateKeepAliveState() {
keepAliveState.decrement()
}

func sendRequestTooLongResponse(context: ChannelHandlerContext) {
let statusDescription = HTTP.statusCodes[HTTPStatusCode.requestTooLong.rawValue] ?? ""
do {
serverResponse = HTTPServerResponse(channel: context.channel, handler: self)
errorResponseSent = true
try serverResponse?.end(with: .requestTooLong, message: statusDescription)
} catch {
Log.error("Failed to send error response")
}
}
}
19 changes: 15 additions & 4 deletions Sources/KituraNet/HTTP/HTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import LoggerAPI
import NIOWebSocket
import CLinuxHelpers
import NIOExtras
import Foundation
import NIOConcurrencyHelpers

#if os(Linux)
import Glibc
Expand Down Expand Up @@ -127,6 +129,12 @@ public class HTTPServer: Server {

var quiescingHelper: ServerQuiescingHelper?

/// server configuration
public var serverConfig: HTTPServerConfiguration

//counter for no of connections
var connectionCount = Atomic(value: 0)

/**
Creates an HTTP server object.

Expand All @@ -136,13 +144,14 @@ public class HTTPServer: Server {
server.listen(on: 8080)
````
*/
public init() {
public init(serverConfig: HTTPServerConfiguration = .default) {
#if os(Linux)
let numberOfCores = Int(linux_sched_getaffinity())
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: numberOfCores > 0 ? numberOfCores : System.coreCount)
#else
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
#endif
self.serverConfig = serverConfig
}

/**
Expand Down Expand Up @@ -309,15 +318,17 @@ public class HTTPServer: Server {
}
.childChannelInitializer { channel in
let httpHandler = HTTPRequestHandler(for: self)
let serverConfigurationHandler = HTTPServerConfigurationHandler(for: self)
let config: NIOHTTPServerUpgradeConfiguration = (upgraders: upgraders, completionHandler: { _ in
_ = channel.pipeline.removeHandler(httpHandler)
})
return channel.pipeline.configureHTTPServerPipeline(withServerUpgrade: config, withErrorHandling: true).flatMap {
if let nioSSLServerHandler = self.createNIOSSLServerHandler() {
_ = channel.pipeline.addHandler(nioSSLServerHandler, position: .first)
return channel.pipeline.addHandler(nioSSLServerHandler, position: .first)
}
return channel.pipeline.addHandler(httpHandler)
}
return channel.eventLoop.makeSucceededFuture(()) } .flatMap {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just drop the return channel.eventLoop.makeSucceededFuture(()) } .flatMap {

return channel.pipeline.addHandler(serverConfigurationHandler, position: .first) }.flatMap {
return channel.pipeline.addHandler(httpHandler)}
}

let listenerDescription: String
Expand Down
47 changes: 47 additions & 0 deletions Sources/KituraNet/HTTP/HTTPServerConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright IBM Corporation 2019
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Foundation

public struct HTTPServerConfiguration {
/// Defines the maximum size of an incoming request, in bytes. If requests are received that are larger
/// than this limit, they will be rejected and the connection will be closed.

/// A value of `nil` means no limit.
public let requestSizeLimit: Int?

/// Defines the maximum number of concurrent connections that a server should accept. Clients attempting
/// to connect when this limit has been reached will be rejected.
/// A value of `nil` means no limit.
public let connectionLimit: Int?

/// A default limit of 1mb on the size of requests that a server should accept
/// A default limit of 10,000 on the number of concurrent connections that a server should accept.
public static var `default` = HTTPServerConfiguration(requestSizeLimit: 1048576, connectionLimit: 10000)


/// Create an `HTTPServerConfiguration` to determine the behaviour of a `Server`.
///
/// - parameter requestSizeLimit: The maximum size of an incoming request. Defaults to `IncomingSocketOptions.defaultRequestSizeLimit`.
/// - parameter connectionLimit: The maximum number of concurrent connections. Defaults to `IncomingSocketOptions.defaultConnectionLimit`.

public init(requestSizeLimit: Int?, connectionLimit: Int?)
{
self.requestSizeLimit = requestSizeLimit
self.connectionLimit = connectionLimit
}

}
74 changes: 74 additions & 0 deletions Sources/KituraNet/HTTP/HTTPServerConfigurationHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import NIO
import NIOHTTP1
import NIOWebSocket
import LoggerAPI
import Foundation
import Dispatch
import NIOConcurrencyHelpers

internal class HTTPServerConfigurationHandler: ChannelDuplexHandler {
// The HTTPServer instance on which this handler is installed
var server: HTTPServer
let requestSizeLimit: Int?
let connectionLimit: Int?
var requestSize: Int = 0
var connectionCount = 0
typealias InboundIn = ByteBuffer
typealias OutboundIn = ByteBuffer
typealias InboundOut = ByteBuffer
typealias OutboundOut = ByteBuffer

public init(for server: HTTPServer) {
self.server = server
// if let requestSizeLimit = server.serverConfig.requestSizeLimit {
// self.requestSizeLimit = requestSizeLimit
// }
// if let connectionLimit = server.serverConfig.connectionLimit {
// self.connectionLimit = connectionLimit
// }
self.requestSizeLimit = server.serverConfig.requestSizeLimit ?? nil
self.connectionLimit = server.serverConfig.connectionLimit ?? nil
}
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let data = self.unwrapInboundIn(data)
requestSize = requestSize + data.readableBytes
if let requestSizeLimit1 = requestSizeLimit {
if requestSize > requestSizeLimit1 {
let statusDescription = HTTP.statusCodes[HTTPStatusCode.requestTooLong.rawValue] ?? ""
var discriptionBuffer = ByteBufferAllocator().buffer(capacity: requestSizeLimit!)
discriptionBuffer.writeString(statusDescription)
context.writeAndFlush(NIOAny(discriptionBuffer),promise: nil)
requestSize = 0
context.close(mode: .all, promise: nil)
return
}
}
context.fireChannelRead(wrapInboundOut(data))
}

public func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
requestSize = event as? Int ?? 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RudraniW there are a number of issues here:

  • the event type should be a type private to you
  • if the event is of any other type, you should forward it on as you can't expect that you're the only one sending outbound user events (same for inbounds)

But really you're making your life unnecessarily hard. You should be doing the request body size limiting after the HTTP parser. Then the whole limiting is pretty straightforward. As an example, see the handler below:

final class HTTPRequestBodySizeLimiter: ChannelInboundHandler {
    typealias InboundIn = HTTPServerRequestPart
    typealias InboundOut = HTTPServerRequestPart

    private var bytesReceivedSoFar: Int
    private let maximumAllowedBytes: Int

    init() {
        self.bytesReceivedSoFar = 0
        self.maximumAllowedBytes = 1024 * 1024
    }

    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        switch self.unwrapInboundIn(data) {
        case .head:
            // at this point in time we must have received 0 bytes
            assert(self.bytesReceivedSoFar == 0)
        case .body(let newBytes):
            guard self.bytesReceivedSoFar + newBytes.readableBytes <= self.maximumAllowedBytes else {
                // for this example, let's just close.
                context.close(promise: nil)
                return
            }
            self.bytesReceivedSoFar += newBytes.readableBytes
        case .end:
            // let's reset the bytes received so far
            self.bytesReceivedSoFar = 0
        }
        // let's forward whatever we have received
        context.fireChannelRead(data)
    }
}

The way you would integrate that is

channel.pipeline.configureHTTPServerPipeline(...).flatMap {
    channel.pipeline.addHandler(HTTPRequestBodySizeLimiter(...))
}.flatMap {
    channel.pipeline.addHandler(httpHandler)
}

Does this make sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weissi The objective here was to prevent oversized requests from exhausting the memory on the server. The reason I suggested the approach of limiting before HTTP header parsing is that a misbehaving client could (deliberately or otherwise) send arbitrary amounts of data as HTTP headers.

I believe that many HTTP servers have a separate limit on the size of the HTTP headers (Apache for example), but Kitura does not (at least, not via Kitura-net). Does Swift-NIO provide a way to limit the size of the headers it accepts? If so, then imposing separate limits on headers and body would be a superior approach.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djones6 SwiftNIO does have a non-configurable 80kiB limit on the total size of all headers.

If you can find anything where SwiftNIO buffers indefinite amounts of anything in memory, please contact us and we will treat it like a security issue.

There's no need to limit anything in front of NIO's HTTP parser or any other parsers.

To make it easy for your own parsers not to grow infinitely in memory, NIO's ByteToMessageHandler supports a maxBufferSize so you as the user can configure any ByteToMessageDecoder to not consume more than a fixed amount of buffer space.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RudraniW I think the solution here should probably be two limits: the request header size and the body size (we'd need to implement this in Kitura-net, but that should be a relatively straightforward change on top of the existing prototype).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djones6 you mean two limits for Kitura-net or Kitura-NIO? Because I think for Kitura-NIO you only need to limit what you buffer (the body). Pretty much every web browser/server has a limit and 80 kiB seems large enough for everybody & small enough that it doesn't matter (we took the same value as NodeJS/nginx because it's in http_parser). It doesn't make sense to limit the header block even further because 80 kiB is smaller than most kernel's receive buffers. So even if you limit it in user-space it's in memory already anyway and there's nothing you can do about it. For Kitura-net I'd suggest also just picking an arbitrary value of something like 80kiB, don't think it needs to be configurable tbh.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djones6 btw, there are other things in HTTP/1 that need limiting: the URL and the status description. In NIO we count that towards the header size. So we treat all of those the same:

GET /veeeeeeeeerrrrryyyy_long_path_that_might_be_multiple_gigabytes_long [...]
GET /short/path HTTP/1.1
but: veeeerrryyyyy-long
or: very
many: many
many-many: headers

or if we parse responses there can also be

HTTP/1.1 200 okaayyyyyyyyy-with-manyyyy-bytes-potentially-multiple-gigabytes

So more precisely, NIO's limit is everything until we see \r\n\r\n that needs to be maintained must fit in 80 kiB. Everything after that we stream to you. Oh, and for the trailers the same 80 kiB limit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'll make sense then for Kitura-net to match NIO's 80kb default for headers. That's also how I would implement it for Kitura-net, which uses CHTTPParser, so we'd impose a header read limit up to the point where the parser indicates that headers are complete (method, path, headers), after which we'd impose the body limit on anything after that.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djones6 cool, if you use CHTTPParser (the one from Nods.js), then you'll have the 80kb limit by default (unless you changed it).

}
public func channelActive(context: ChannelHandlerContext) {
_ = server.connectionCount.add(1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to restrict "connections" or "currently active requests"? With HTTP/1.1 keep-alive those two are different. You're implementing limiting "connections", is that what you want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback @weissi. For now I am implementing limiting "connections". But may be in future I will look into limiting "currently active requests".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decision to limit the number of connections here was largely based on what could be easily implemented in Kitura-net. Limiting active requests is more attractive, but might take some extra work to implement on -net (when waking up a reader thread, we'd need to check if we were allowed to process another request, and have a way to defer the read(s) until later).

Though this doesn't necessarily preclude us from implementing it in Kitura-NIO - the configuration would need to be consistent across the two, but we could mark it as unsupported on -net.

if let connectionLimit1 = connectionLimit {
if server.connectionCount.load() > connectionLimit1{
let statusDescription = HTTP.statusCodes[HTTPStatusCode.serviceUnavailable.rawValue] ?? ""
var statusBuffer = ByteBufferAllocator().buffer(capacity: 1024)
statusBuffer.writeString(statusDescription)
context.writeAndFlush(NIOAny(statusBuffer),promise: nil)
_ = server.connectionCount.sub(1)
context.close(mode: .all, promise: nil)
return
}
}
context.fireChannelActive()
}

func channelInactive(context: ChannelHandlerContext) {
_ = server.connectionCount.sub(1)
context.fireChannelInactive()
}
}


24 changes: 24 additions & 0 deletions Tests/KituraNetTests/HTTPResponseHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import NIO
import NIOHTTP1
import NIOWebSocket
import LoggerAPI
import Foundation
import Dispatch
import XCTest
@testable import KituraNet
internal class HTTPConfigTestsResponseHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
let expectedSubstring: String
let expectation: XCTestExpectation
public init(expectation: XCTestExpectation, expectedSubstring: String ){
self.expectedSubstring = expectedSubstring
self.expectation = expectation
}

public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let response = self.unwrapInboundIn(data)
XCTAssert(response.getString(at: 0, length: response.readableBytes)?.starts(with: expectedSubstring) ?? false)
expectation.fulfill()
}
}

2 changes: 1 addition & 1 deletion Tests/KituraNetTests/HTTPResponseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class HTTPResponseTests: KituraNetTest {
let headers = HeadersContainer()

headers.append("Content-Type", value: "text/html")
var values = headers["Content-Type"]
let values = headers["Content-Type"]
XCTAssertNotNil(values, "Couldn't retrieve just set Content-Type header")
XCTAssertEqual(values?.count, 1, "Content-Type header should only have one value")
XCTAssertEqual(values?[0], "text/html")
Expand Down
28 changes: 14 additions & 14 deletions Tests/KituraNetTests/KituraNIOTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ class KituraNetTest: XCTestCase {
}
}

func startServer(_ delegate: ServerDelegate?, unixDomainSocketPath: String? = nil, port: Int = portDefault, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault) throws -> HTTPServer {

let server = HTTP.createServer()
func startServer(_ delegate: ServerDelegate?, unixDomainSocketPath: String? = nil, port: Int = portDefault, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, serverConfig: HTTPServerConfiguration = .default) throws -> HTTPServer {
let serverConfig = serverConfig
let server = HTTP.createServer(serverConfig: serverConfig)
server.delegate = delegate
if useSSL {
server.sslConfig = KituraNetTest.sslConfig
Expand All @@ -103,8 +103,9 @@ class KituraNetTest: XCTestCase {

/// Convenience function for starting an HTTPServer on an ephemeral port,
/// returning the a tuple containing the server and the port it is listening on.
func startEphemeralServer(_ delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault) throws -> (server: HTTPServer, port: Int) {
let server = try startServer(delegate, port: 0, useSSL: useSSL, allowPortReuse: allowPortReuse)
func startEphemeralServer(_ delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, serverConfig: HTTPServerConfiguration = .default) throws -> (server: HTTPServer, port: Int) {
let serverConfig = serverConfig
let server = try startServer(delegate, port: 0, useSSL: useSSL, allowPortReuse: allowPortReuse, serverConfig: serverConfig)
guard let serverPort = server.port else {
throw KituraNetTestError(message: "Server port was not initialized")
}
Expand All @@ -121,26 +122,26 @@ class KituraNetTest: XCTestCase {
case both
}

func performServerTest(_ delegate: ServerDelegate?, socketType: SocketType = .both, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: (XCTestExpectation) -> Void...) {
func performServerTest(serverConfig: HTTPServerConfiguration = .default, _ delegate: ServerDelegate?, socketType: SocketType = .both, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: (XCTestExpectation) -> Void...) {
let serverConfig = serverConfig
self.socketType = socketType
if socketType != .tcp {
performServerTestWithUnixSocket(delegate: delegate, useSSL: useSSL, allowPortReuse: allowPortReuse, line: line, asyncTasks: asyncTasks)
performServerTestWithUnixSocket(serverConfig: serverConfig, delegate: delegate, useSSL: useSSL, allowPortReuse: allowPortReuse, line: line, asyncTasks: asyncTasks)
}
if socketType != .unixDomainSocket {
performServerTestWithTCPPort(delegate: delegate, useSSL: useSSL, allowPortReuse: allowPortReuse, line: line, asyncTasks: asyncTasks)
performServerTestWithTCPPort(serverConfig: serverConfig, delegate: delegate, useSSL: useSSL, allowPortReuse: allowPortReuse, line: line, asyncTasks: asyncTasks)
}
}

func performServerTestWithUnixSocket(delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: [(XCTestExpectation) -> Void]) {
func performServerTestWithUnixSocket(serverConfig: HTTPServerConfiguration = .default, delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: [(XCTestExpectation) -> Void]) {
do {
var server: HTTPServer
self.useSSL = useSSL
self.unixDomainSocketPath = self.socketFilePath
server = try startServer(delegate, unixDomainSocketPath: self.unixDomainSocketPath, useSSL: useSSL, allowPortReuse: allowPortReuse)
server = try startServer(delegate, unixDomainSocketPath: self.unixDomainSocketPath, useSSL: useSSL, allowPortReuse: allowPortReuse, serverConfig: serverConfig)
defer {
server.stop()
}

let requestQueue = DispatchQueue(label: "Request queue")
for (index, asyncTask) in asyncTasks.enumerated() {
let expectation = self.expectation(line: line, index: index)
Expand All @@ -158,18 +159,17 @@ class KituraNetTest: XCTestCase {
}
}

func performServerTestWithTCPPort(delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: [(XCTestExpectation) -> Void]) {
func performServerTestWithTCPPort(serverConfig: HTTPServerConfiguration = .default, delegate: ServerDelegate?, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault, line: Int = #line, asyncTasks: [(XCTestExpectation) -> Void]) {
do {
var server: HTTPServer
var ephemeralPort: Int = 0
self.useSSL = useSSL
(server, ephemeralPort) = try startEphemeralServer(delegate, useSSL: useSSL, allowPortReuse: allowPortReuse)
(server, ephemeralPort) = try startEphemeralServer(delegate, useSSL: useSSL, allowPortReuse: allowPortReuse, serverConfig: serverConfig)
self.port = ephemeralPort
self.unixDomainSocketPath = nil
defer {
server.stop()
}

let requestQueue = DispatchQueue(label: "Request queue")
for (index, asyncTask) in asyncTasks.enumerated() {
let expectation = self.expectation(line: line, index: index)
Expand Down
2 changes: 1 addition & 1 deletion Tests/KituraNetTests/LargePayloadTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class LargePayloadTests: KituraNetTest {
private let delegate = TestServerDelegate()

func testLargePosts() {
performServerTest(delegate, useSSL: false, asyncTasks: { expectation in
performServerTest(serverConfig: HTTPServerConfiguration(requestSizeLimit: 100*10000, connectionLimit: 1),delegate,socketType: .tcp, useSSL: false, asyncTasks: { expectation in
let payload = "[" + contentTypesString + "," + contentTypesString + contentTypesString + "," + contentTypesString + "]"
self.performRequest("post", path: "/largepost", callback: {response in
XCTAssertEqual(response?.statusCode, HTTPStatusCode.OK, "Status code wasn't .Ok was \(String(describing: response?.statusCode))")
Expand Down
Loading