Skip to content

Commit

Permalink
Disable port reuse by default (#223)
Browse files Browse the repository at this point in the history
Pass `allowPortReuse: false` by default to `Socket.listen()`
  • Loading branch information
djones6 committed Oct 17, 2017
1 parent cb09ae6 commit e04ee59
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 12 deletions.
6 changes: 5 additions & 1 deletion Sources/KituraNet/FastCGI/FastCGIServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class FastCGIServer: Server {

/// TCP socket used for listening for new connections
private var listenSocket: Socket?

/// Whether or not this server allows port reuse (default: disallowed)
public var allowPortReuse: Bool = false

fileprivate let lifecycleListener = ServerLifecycleListener()

Expand All @@ -74,8 +77,9 @@ public class FastCGIServer: Server {
let socket = try Socket.create()
self.listenSocket = socket

try socket.listen(on: port, maxBacklogSize: maxPendingConnections)
try socket.listen(on: port, maxBacklogSize: maxPendingConnections, allowPortReuse: self.allowPortReuse)
Log.info("Listening on port \(port)")
Log.verbose("Options for port \(port): maxPendingConnections: \(maxPendingConnections), allowPortReuse: \(self.allowPortReuse)")

// set synchronously to avoid contention in back to back server start/stop calls
self.state = .started
Expand Down
7 changes: 6 additions & 1 deletion Sources/KituraNet/HTTP/HTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public class HTTPServer: Server {
/// TCP socket used for listening for new connections
private var listenSocket: Socket?

/// Whether or not this server allows port reuse (default: disallowed)
public var allowPortReuse: Bool = false

/// Maximum number of pending connections
private let maxPendingConnections = 100

Expand Down Expand Up @@ -92,7 +95,7 @@ public class HTTPServer: Server {
socket.delegate = try SSLService(usingConfiguration: sslConfig);
}

try socket.listen(on: port, maxBacklogSize: maxPendingConnections)
try socket.listen(on: port, maxBacklogSize: maxPendingConnections, allowPortReuse: self.allowPortReuse)

let socketManager = IncomingSocketManager()
self.socketManager = socketManager
Expand All @@ -116,8 +119,10 @@ public class HTTPServer: Server {
#endif

Log.info("Listening on port \(self.port!) (delegate: \(delegate))")
Log.verbose("Options for port \(self.port!): delegate: \(delegate), maxPendingConnections: \(maxPendingConnections), allowPortReuse: \(self.allowPortReuse)")
} else {
Log.info("Listening on port \(self.port!)")
Log.verbose("Options for port \(self.port!): maxPendingConnections: \(maxPendingConnections), allowPortReuse: \(self.allowPortReuse)")
}

// set synchronously to avoid contention in back to back server start/stop calls
Expand Down
20 changes: 10 additions & 10 deletions Tests/KituraNetTests/KituraNetTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class KituraNetTest: XCTestCase {

static let useSSLDefault = true
static let portDefault = 8080
static let portReuseDefault = false

var useSSL = useSSLDefault
var port = portDefault
Expand Down Expand Up @@ -56,28 +57,26 @@ class KituraNetTest: XCTestCase {
func doTearDown() {
}

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

let server: HTTPServer
let server = HTTP.createServer()
server.delegate = delegate
server.allowPortReuse = allowPortReuse
if useSSL {
server = HTTP.createServer()
server.delegate = delegate
server.sslConfig = KituraNetTest.sslConfig
try server.listen(on: port)
} else {
server = try HTTPServer.listen(on: port, delegate: delegate)
}
try server.listen(on: port)
return server
}

func performServerTest(_ delegate: ServerDelegate?, port: Int = portDefault, useSSL: Bool = useSSLDefault,
func performServerTest(_ delegate: ServerDelegate?, port: Int = portDefault, useSSL: Bool = useSSLDefault, allowPortReuse: Bool = portReuseDefault,
line: Int = #line, asyncTasks: (XCTestExpectation) -> Void...) {

do {
self.useSSL = useSSL
self.port = port

let server: HTTPServer = try startServer(delegate, port: port, useSSL: useSSL)
let server: HTTPServer = try startServer(delegate, port: port, useSSL: useSSL, allowPortReuse: allowPortReuse)
defer {
server.stop()
}
Expand All @@ -99,13 +98,14 @@ class KituraNetTest: XCTestCase {
}
}

func performFastCGIServerTest(_ delegate: ServerDelegate?, port: Int = portDefault,
func performFastCGIServerTest(_ delegate: ServerDelegate?, port: Int = portDefault, allowPortReuse: Bool = portReuseDefault,
line: Int = #line, asyncTasks: (XCTestExpectation) -> Void...) {

do {
self.port = port

let server = try FastCGIServer.listen(on: port, delegate: delegate)
server.allowPortReuse = allowPortReuse
defer {
server.stop()
}
Expand Down
59 changes: 59 additions & 0 deletions Tests/KituraNetTests/RegressionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,63 @@ class RegressionTests: KituraNetTest {
}

}

/// Tests that attempting to start a second HTTPServer on the same port fails.
func testServersCollidingOnPort() {
do {
let server: HTTPServer = try startServer(nil, port: 0, useSSL: false)
defer {
server.stop()
}

guard let serverPort = server.port else {
XCTFail("Server port was not initialized")
return
}
XCTAssertTrue(serverPort != 0, "Ephemeral server port not set")

do {
let collidingServer: HTTPServer = try startServer(nil, port: serverPort, useSSL: false)
defer {
collidingServer.stop()
}
XCTFail("Server unexpectedly succeeded in listening on a port already in use")
} catch {
XCTAssert(error is Socket.Error, "Expected a Socket.Error, received: \(error)")
}

} catch {
XCTFail("Error: \(error)")
}
}

/// Tests that attempting to start a second HTTPServer on the same port with
/// SO_REUSEPORT enabled is successful.
func testServersSharingPort() {
do {
let server: HTTPServer = try startServer(nil, port: 0, useSSL: false, allowPortReuse: true)
defer {
server.stop()
}

guard let serverPort = server.port else {
XCTFail("Server port was not initialized")
return
}
XCTAssertTrue(serverPort != 0, "Ephemeral server port not set")

do {
let sharingServer: HTTPServer = try startServer(nil, port: serverPort, useSSL: false, allowPortReuse: true)
defer {
sharingServer.stop()
}
} catch {
XCTFail("Second server could not share listener port, received: \(error)")
}

} catch {
XCTFail("Error: \(error)")
}
}

}

0 comments on commit e04ee59

Please sign in to comment.