<commit_before>/*
 * Copyright IBM Corporation 2015
 *
 * 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 KituraNet
import LoggerAPI

import Foundation
import Dispatch

// MARK Kitura

/**
 Facilities for creating, starting and stopping Kitura-based servers.
 ### Usage Example: ###
 In this example, a `Router` is created, and a single route registered that responds to an HTTP GET request on "/" with a plain text response.
 An HTTP server is created on port 8080, and is started with the `Kitura.run()` function (note that this function does not return).
 The route can then be accessed by visiting `http://localhost:8080`.
 ```swift
 let router = Router()
 router.get("/") { request, response, next in
     response.send("Hello world")
     next()
 }
 Kitura.addHTTPServer(onPort: 8080, with: router)
 Kitura.run()
 ```
 */
public class Kitura {

    // MARK: Create Server
    
    /// Add an HTTPServer on a port with a delegate.
    ///
    /// The server is only registered with the framework, it does not start listening
    /// on the port until `Kitura.run()` or `Kitura.start()` are called.
    ///
    ///### Usage Example: ###
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    ///```
    /// - Parameter onPort: The port to listen on.
    /// - Parameter with: The `ServerDelegate` to use.
    /// - Parameter withSSL: The `sslConfig` to use.
    /// - Parameter keepAlive: The maximum number of additional requests to permit per Keep-Alive connection. Defaults to `.unlimited`. If set to `.disabled`, Keep-Alive will not be permitted.
    /// - Parameter allowPortReuse: Determines whether the listener port may be shared with other Kitura instances (`SO_REUSEPORT`). Defaults to `false`. If the specified port is already in use by another listener that has not allowed sharing, the server will fail to start.
    /// - Returns: The created `HTTPServer`.
    @discardableResult
    public class func addHTTPServer(onPort port: Int,
                                    with delegate: ServerDelegate,
                                    withSSL sslConfig: SSLConfig?=nil,
                                    keepAlive keepAliveState: KeepAliveState = .unlimited,
                                    allowPortReuse: Bool = false) -> HTTPServer {
        let server = HTTP.createServer()
        server.delegate = delegate
        server.sslConfig = sslConfig?.config
        server.keepAliveState = keepAliveState
        server.allowPortReuse = allowPortReuse
        httpServersAndPorts.append((server: server, port: port))
        return server
    }

    /// Add a FastCGIServer on a port with a delegate.
    ///
    /// The server is only registered with the framework, it does not start listening
    /// on the port until `Kitura.run()` or `Kitura.start()` are called.
    ///
    ///### Usage Example: ###
    ///```swift
    /// let router = Router()
    /// Kitura.addFastCGIServer(onPort: 8080, with: router)
    ///```
    /// - Parameter onPort: The port to listen on.
    /// - Parameter with: The `ServerDelegate` to use.
    /// - Parameter allowPortReuse: Determines whether the listener port may be shared with other Kitura instances (`SO_REUSEPORT`). Defaults to `false`. If the specified port is already in use by another listener that has not allowed sharing, the server will fail to start.
    /// - Returns: The created `FastCGIServer`.
    @discardableResult
    public class func addFastCGIServer(onPort port: Int,
                                       with delegate: ServerDelegate,
                                       allowPortReuse: Bool = false) -> FastCGIServer {
        let server = FastCGI.createServer()
        server.delegate = delegate
        server.allowPortReuse = allowPortReuse
        fastCGIServersAndPorts.append((server: server, port: port))
        return server
    }

    // MARK: Start Servers
    
    /// Start the Kitura framework.
    ///
    ///### Usage Example: ###
    /// Make all registered servers start listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.run()
    ///```
    /// - note: This function never returns - it should be the last call in your `main.swift` file.
    public class func run() {
        Log.verbose("Starting Kitura framework...")
        start()
        ListenerGroup.waitForListeners()
    }

    /// Start all registered servers and return.
    ///
    ///### Usage Example: ###
    /// Make all registered servers start listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.start()
    ///```
    public class func start() {
        for (server, port) in httpServersAndPorts {
            Log.verbose("Starting an HTTP Server on port \(port)...")
            do {
                try server.listen(on: port)
            } catch {
                Log.error("Error listening on port \(port): \(error). Use server.failed(callback:) to handle")
            }
        }
        for (server, port) in fastCGIServersAndPorts {
            Log.verbose("Starting a FastCGI Server on port \(port)...")
            do {
                try server.listen(on: port)
            } catch {
                Log.error("Error listening on port \(port): \(error). Use server.failed(callback:) to handle")
            }
        }
    }

    // MARK: Stop Servers
    
    /// Stop all registered servers.
    ///
    ///### Usage Example: ###
    /// Make all registered servers stop listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.start()
    /// Kitura.stop()
    ///```
    ///
    /// - Parameter unregister: If servers should be unregistered after they are stopped (default true).
    public class func stop(unregister: Bool = true) {
        for (server, port) in httpServersAndPorts {
            Log.verbose("Stopping HTTP Server on port \(port)...")
            server.stop()
        }

        for (server, port) in fastCGIServersAndPorts {
            Log.verbose("Stopping FastCGI Server on port \(port)...")
            server.stop()
        }

        if unregister {
            httpServersAndPorts.removeAll()
            fastCGIServersAndPorts.removeAll()
        }
    }

    typealias Port = Int
    internal private(set) static var httpServersAndPorts = [(server: HTTPServer, port: Port)]()
    internal private(set) static var fastCGIServersAndPorts = [(server: FastCGIServer, port: Port)]()
}<commit_msg>Fix: Add locking for Kitura servers (#1377)<commit_after>/*
 * Copyright IBM Corporation 2015
 *
 * 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 KituraNet
import LoggerAPI

import Foundation
import Dispatch

// MARK Kitura

/**
 Facilities for creating, starting and stopping Kitura-based servers.
 ### Usage Example: ###
 In this example, a `Router` is created, and a single route registered that responds to an HTTP GET request on "/" with a plain text response.
 An HTTP server is created on port 8080, and is started with the `Kitura.run()` function (note that this function does not return).
 The route can then be accessed by visiting `http://localhost:8080`.
 ```swift
 let router = Router()
 router.get("/") { request, response, next in
     response.send("Hello world")
     next()
 }
 Kitura.addHTTPServer(onPort: 8080, with: router)
 Kitura.run()
 ```
 */
public class Kitura {

    // MARK: Create Server
    
    /// Add an HTTPServer on a port with a delegate.
    ///
    /// The server is only registered with the framework, it does not start listening
    /// on the port until `Kitura.run()` or `Kitura.start()` are called.
    ///
    ///### Usage Example: ###
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    ///```
    /// - Parameter onPort: The port to listen on.
    /// - Parameter with: The `ServerDelegate` to use.
    /// - Parameter withSSL: The `sslConfig` to use.
    /// - Parameter keepAlive: The maximum number of additional requests to permit per Keep-Alive connection. Defaults to `.unlimited`. If set to `.disabled`, Keep-Alive will not be permitted.
    /// - Parameter allowPortReuse: Determines whether the listener port may be shared with other Kitura instances (`SO_REUSEPORT`). Defaults to `false`. If the specified port is already in use by another listener that has not allowed sharing, the server will fail to start.
    /// - Returns: The created `HTTPServer`.
    @discardableResult
    public class func addHTTPServer(onPort port: Int,
                                    with delegate: ServerDelegate,
                                    withSSL sslConfig: SSLConfig?=nil,
                                    keepAlive keepAliveState: KeepAliveState = .unlimited,
                                    allowPortReuse: Bool = false) -> HTTPServer {
        let server = HTTP.createServer()
        server.delegate = delegate
        server.sslConfig = sslConfig?.config
        server.keepAliveState = keepAliveState
        server.allowPortReuse = allowPortReuse
        serverLock.lock()
        httpServersAndPorts.append((server: server, port: port))
        serverLock.unlock()
        return server
    }

    /// Add a FastCGIServer on a port with a delegate.
    ///
    /// The server is only registered with the framework, it does not start listening
    /// on the port until `Kitura.run()` or `Kitura.start()` are called.
    ///
    ///### Usage Example: ###
    ///```swift
    /// let router = Router()
    /// Kitura.addFastCGIServer(onPort: 8080, with: router)
    ///```
    /// - Parameter onPort: The port to listen on.
    /// - Parameter with: The `ServerDelegate` to use.
    /// - Parameter allowPortReuse: Determines whether the listener port may be shared with other Kitura instances (`SO_REUSEPORT`). Defaults to `false`. If the specified port is already in use by another listener that has not allowed sharing, the server will fail to start.
    /// - Returns: The created `FastCGIServer`.
    @discardableResult
    public class func addFastCGIServer(onPort port: Int,
                                       with delegate: ServerDelegate,
                                       allowPortReuse: Bool = false) -> FastCGIServer {
        let server = FastCGI.createServer()
        server.delegate = delegate
        server.allowPortReuse = allowPortReuse
        serverLock.lock()
        fastCGIServersAndPorts.append((server: server, port: port))
        serverLock.unlock()
        return server
    }

    // MARK: Start Servers
    
    /// Start the Kitura framework.
    ///
    ///### Usage Example: ###
    /// Make all registered servers start listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.run()
    ///```
    /// - note: This function never returns - it should be the last call in your `main.swift` file.
    public class func run() {
        Log.verbose("Starting Kitura framework...")
        start()
        ListenerGroup.waitForListeners()
    }

    /// Start all registered servers and return.
    ///
    ///### Usage Example: ###
    /// Make all registered servers start listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.start()
    ///```
    public class func start() {
        serverLock.lock()
        for (server, port) in httpServersAndPorts {
            Log.verbose("Starting an HTTP Server on port \(port)...")
            do {
                try server.listen(on: port)
            } catch {
                Log.error("Error listening on port \(port): \(error). Use server.failed(callback:) to handle")
            }
        }
        for (server, port) in fastCGIServersAndPorts {
            Log.verbose("Starting a FastCGI Server on port \(port)...")
            do {
                try server.listen(on: port)
            } catch {
                Log.error("Error listening on port \(port): \(error). Use server.failed(callback:) to handle")
            }
        }
        serverLock.unlock()
    }

    // MARK: Stop Servers
    
    /// Stop all registered servers.
    ///
    ///### Usage Example: ###
    /// Make all registered servers stop listening on their port.
    ///```swift
    /// let router = Router()
    /// Kitura.addHTTPServer(onPort: 8080, with: router)
    /// Kitura.start()
    /// Kitura.stop()
    ///```
    ///
    /// - Parameter unregister: If servers should be unregistered after they are stopped (default true).
    public class func stop(unregister: Bool = true) {
        serverLock.lock()
        for (server, port) in httpServersAndPorts {
            Log.verbose("Stopping HTTP Server on port \(port)...")
            server.stop()
        }

        for (server, port) in fastCGIServersAndPorts {
            Log.verbose("Stopping FastCGI Server on port \(port)...")
            server.stop()
        }

        if unregister {
            httpServersAndPorts.removeAll()
            fastCGIServersAndPorts.removeAll()
        }
        serverLock.unlock()
    }

    typealias Port = Int
    private static let serverLock = NSLock()
    internal private(set) static var httpServersAndPorts = [(server: HTTPServer, port: Port)]()
    internal private(set) static var fastCGIServersAndPorts = [(server: FastCGIServer, port: Port)]()
}
{'fraction_non_alphanumeric': 0.11182633705403, 'fraction_numerical': 0.005752242689858248, 'mean_word_length': 2.210375906792702, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7629687', 'n_tokens_mistral': 4014, 'n_tokens_neox': 3844, 'n_words': 1429}