From 5fc06b21e8e615147fa8341c249ae010a1b41fba Mon Sep 17 00:00:00 2001 From: TaborKelly Date: Fri, 24 May 2019 01:21:38 -0700 Subject: [PATCH] Allow Kitura-Net to only listen on one network address (#299) --- Sources/KituraNet/FastCGI/FastCGIServer.swift | 29 +++++++++++------- Sources/KituraNet/HTTP/HTTPServer.swift | 28 +++++++++++------ Sources/KituraNet/Server/Server.swift | 30 +++++++++++++++++++ Tests/KituraNetTests/ClientE2ETests.swift | 2 +- Tests/KituraNetTests/KituraNetTest.swift | 4 +-- .../LifecycleListenerTests.swift | 8 ++--- Tests/KituraNetTests/MonitoringTests.swift | 2 +- 7 files changed, 76 insertions(+), 27 deletions(-) diff --git a/Sources/KituraNet/FastCGI/FastCGIServer.swift b/Sources/KituraNet/FastCGI/FastCGIServer.swift index d0b4037..9409e14 100644 --- a/Sources/KituraNet/FastCGI/FastCGIServer.swift +++ b/Sources/KituraNet/FastCGI/FastCGIServer.swift @@ -56,6 +56,10 @@ public class FastCGIServer: Server { */ public private(set) var port: Int? + /// The address of the network interface to listen on. Defaults to nil, which means this server will listen on all + /// interfaces. + public private(set) var address: String? + /** A server state. @@ -108,14 +112,17 @@ public class FastCGIServer: Server { Listens for connections on a socket - Parameter on: port number for new connections - + - Parameter address: The address of a network interface to listen on, for example "localhost". The default is nil, + which listens for connections on all interfaces. + ### Usage Example: ### ````swift - try server.listen(on: port) + try server.listen(on: port, address: "localhost") ```` */ - public func listen(on port: Int) throws { + public func listen(on port: Int, address: String? = nil) throws { self.port = port + self.address = address do { let socket = try Socket.create() self.listenSocket = socket @@ -144,24 +151,26 @@ public class FastCGIServer: Server { /** Static method to create a new `FastCGIServer` and have it listen for conenctions - + - Parameter on: port number for accepting new connections + - Parameter address: The address of a network interface to listen on, for example "localhost". The default is nil, + which listens for connections on all interfaces. - Parameter delegate: the delegate handler for FastCGI/HTTP connections - + - Returns: a new `FastCGIServer` instance - + ### Usage Example: ### ````swift - let server = try FastCGIServer.listen(on: port, delegate: delegate) + let server = try FastCGIServer.listen(on: port, address: "localhost", delegate: delegate) ```` */ - public static func listen(on port: Int, delegate: ServerDelegate?) throws -> FastCGIServer { + public static func listen(on port: Int, address: String? = nil, delegate: ServerDelegate?) throws -> FastCGIServer { let server = FastCGI.createServer() server.delegate = delegate - try server.listen(on: port) + try server.listen(on: port, address: address) return server } - + /** Listens for connections on a socket diff --git a/Sources/KituraNet/HTTP/HTTPServer.swift b/Sources/KituraNet/HTTP/HTTPServer.swift index 72500a4..cf96245 100644 --- a/Sources/KituraNet/HTTP/HTTPServer.swift +++ b/Sources/KituraNet/HTTP/HTTPServer.swift @@ -55,12 +55,16 @@ public class HTTPServer: Server { /// The TCP port on which this server listens for new connections. If `nil`, this server does not listen on a TCP socket. public private(set) var port: Int? + /// The address of the network interface to listen on. Defaults to nil, which means this server will listen on all + /// interfaces. + public private(set) var address: String? + /// The Unix domain socket path on which this server listens for new connections. If `nil`, this server does not listen on a Unix socket. public private(set) var unixDomainSocketPath: String? /// The types of listeners we currently support. private enum ListenerType { - case inet(Int) + case inet(Int, String?) // port, node case unix(String) } @@ -151,14 +155,17 @@ public class HTTPServer: Server { ### Usage Example: ### ````swift - try server.listen(on: 8080) + try server.listen(on: 8080, address: "localhost") ```` - Parameter port: Port number for new connections, e.g. 8080 + - Parameter address: The address of a network interface to listen on, for example "localhost". The default is nil, + which listens for connections on all interfaces. */ - public func listen(on port: Int) throws { + public func listen(on port: Int, address: String? = nil) throws { self.port = port - try listen(.inet(port)) + self.address = address + try listen(.inet(port, address)) } /** @@ -195,8 +202,9 @@ public class HTTPServer: Server { let listenerDescription: String switch listener { - case .inet(let port): - try socket.listen(on: port, maxBacklogSize: maxPendingConnections, allowPortReuse: self.allowPortReuse) + case .inet(let port, let node): + try socket.listen(on: port, maxBacklogSize: maxPendingConnections, allowPortReuse: self.allowPortReuse, + node: node) // If a random (ephemeral) port number was requested, get the listening port let listeningPort = Int(socket.listeningPort) if listeningPort != port { @@ -253,18 +261,20 @@ public class HTTPServer: Server { ### Usage Example: ### ````swift - let server = HTTPServer.listen(on: 8080, delegate: self) + let server = HTTPServer.listen(on: 8080, address: "localhost", delegate: self) ```` - Parameter on: Port number for accepting new connections. + - Parameter address: The address of a network interface to listen on, for example "localhost". The default is nil, + which listens for connections on all interfaces. - Parameter delegate: The delegate handler for HTTP connections. - Returns: A new instance of a `HTTPServer`. */ - public static func listen(on port: Int, delegate: ServerDelegate?) throws -> HTTPServer { + public static func listen(on port: Int, address: String? = nil, delegate: ServerDelegate?) throws -> HTTPServer { let server = HTTP.createServer() server.delegate = delegate - try server.listen(on: port) + try server.listen(on: port, address: address) return server } diff --git a/Sources/KituraNet/Server/Server.swift b/Sources/KituraNet/Server/Server.swift index 4053eda..5f39733 100644 --- a/Sources/KituraNet/Server/Server.swift +++ b/Sources/KituraNet/Server/Server.swift @@ -26,14 +26,35 @@ public protocol Server { /// Port number for listening for new connections. var port: Int? { get } + /// The address of the network interface to listen on. Defaults to nil, which means this server will listen on all + /// interfaces. + var address: String? { get } + /// A server state. var state: ServerState { get } + /// Listen for connections on a socket. + /// + /// - Parameter on: port number for new connections (eg. 8080) + /// - Parameter address: The address of a network interface to listen on, for example "localhost". The default is + /// nil, which listens for connections on all interfaces. + func listen(on port: Int, address: String?) throws + /// Listen for connections on a socket. /// /// - Parameter on: port number for new connections (eg. 8080) func listen(on port: Int) throws + /// Static method to create a new Server and have it listen for connections. + /// + /// - Parameter on: port number for accepting new connections + /// - Parameter address: The address of a network interface to listen on, for example "localhost". The default is + /// nil, which listens for connections on all interfaces. + /// - Parameter delegate: the delegate handler for HTTP connections + /// + /// - Returns: a new Server instance + static func listen(on port: Int, address: String?, delegate: ServerDelegate?) throws -> ServerType + /// Static method to create a new Server and have it listen for connections. /// /// - Parameter on: port number for accepting new connections @@ -94,3 +115,12 @@ public protocol Server { @discardableResult func clientConnectionFailed(callback: @escaping (Swift.Error) -> Void) -> Self } + +extension Server { + public func listen(on port: Int) throws { + try listen(on: port, address: nil) + } + public static func listen(on port: Int, delegate: ServerDelegate?) throws -> ServerType { + return try Self.listen(on: port, address: nil, delegate: delegate) + } +} diff --git a/Tests/KituraNetTests/ClientE2ETests.swift b/Tests/KituraNetTests/ClientE2ETests.swift index 2ea387c..07f3906 100644 --- a/Tests/KituraNetTests/ClientE2ETests.swift +++ b/Tests/KituraNetTests/ClientE2ETests.swift @@ -202,7 +202,7 @@ class ClientE2ETests: KituraNetTest { func testEphemeralListeningPort() { do { - let server = try HTTPServer.listen(on: 0, delegate: delegate) + let server = try HTTPServer.listen(on: 0, address: "localhost", delegate: delegate) _ = HTTP.get("http://localhost:\(server.port!)") { response in XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") XCTAssertEqual(response?.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(String(describing: response?.statusCode))") diff --git a/Tests/KituraNetTests/KituraNetTest.swift b/Tests/KituraNetTests/KituraNetTest.swift index 2054110..05dce23 100644 --- a/Tests/KituraNetTests/KituraNetTest.swift +++ b/Tests/KituraNetTests/KituraNetTest.swift @@ -81,7 +81,7 @@ class KituraNetTest: XCTestCase { if let unixDomainSocketPath = unixDomainSocketPath { try server.listen(unixDomainSocketPath: unixDomainSocketPath) } else { - try server.listen(on: port) + try server.listen(on: port, address: "localhost") } return server } @@ -163,7 +163,7 @@ class KituraNetTest: XCTestCase { do { self.port = port - let server = try FastCGIServer.listen(on: port, delegate: delegate) + let server = try FastCGIServer.listen(on: port, address: "localhost", delegate: delegate) server.allowPortReuse = allowPortReuse defer { server.stop() diff --git a/Tests/KituraNetTests/LifecycleListenerTests.swift b/Tests/KituraNetTests/LifecycleListenerTests.swift index fb92aa1..2124017 100644 --- a/Tests/KituraNetTests/LifecycleListenerTests.swift +++ b/Tests/KituraNetTests/LifecycleListenerTests.swift @@ -55,7 +55,7 @@ class LifecycleListenerTests: KituraNetTest { } do { - try server.listen(on: self.port) + try server.listen(on: self.port, address: "localhost") self.waitForExpectations(timeout: 5) { error in XCTAssertNil(error) @@ -93,7 +93,7 @@ class LifecycleListenerTests: KituraNetTest { } do { - try server.listen(on: self.port) + try server.listen(on: self.port, address: "localhost") self.waitForExpectations(timeout: 5) { error in XCTAssertNil(error) @@ -127,7 +127,7 @@ class LifecycleListenerTests: KituraNetTest { } do { - try server.listen(on: self.port) + try server.listen(on: self.port, address: "localhost") self.waitForExpectations(timeout: 5) { error in XCTAssertNil(error) @@ -155,7 +155,7 @@ class LifecycleListenerTests: KituraNetTest { }) do { - try server.listen(on: -1) + try server.listen(on: -1, address: nil) } catch { // Do NOT fail the test if an error is thrown. // In this test case an error should be thrown. diff --git a/Tests/KituraNetTests/MonitoringTests.swift b/Tests/KituraNetTests/MonitoringTests.swift index abb6c35..9ac06ae 100644 --- a/Tests/KituraNetTests/MonitoringTests.swift +++ b/Tests/KituraNetTests/MonitoringTests.swift @@ -64,7 +64,7 @@ class MonitoringTests: KituraNetTest { } do { - try server.listen(on: self.port) + try server.listen(on: self.port, address: nil) self.waitForExpectations(timeout: 10) { error in server.stop()