<commit_before>//
//  EpubServer.swift
//  R2Streamer
//
//  Created by <NAME> on 21/12/2016.
//  Copyright © 2016 Readium. All rights reserved.
//

import Foundation
import GCDWebServers

extension EpubServer: Loggable {}

/// Errors thrown by the `EpubServer`.
///
/// - epubParser: An error thrown by the EpubParser.
/// - epubFetcher: An error thrown by the EpubFetcher.
public enum EpubServerError: Error{
    case epubParser(underlayingError: Error)
    case epubFetcher(underlayingError: Error)
}

/// The HTTP server for the publication's manifests and assets. Serves Epubs.
open class EpubServer {

    /// The HTTP server
    var webServer: GCDWebServer

    let parser = EpubParser()

    // FIXME: probably get rid of the server serving multiple epub at a given time
    //          better to implement indexing for multibook search etc
    /// The dictionary of EPUB containers keyed by prefix.
    var containers: [String: Container] = [:]

    /// The dictionaty of publications keyed by prefix.
    var publications: [String: Publication] = [:]

    /// The running HTTP server listening port.
    public var port: UInt? {
        get { return webServer.port }
    }

    /// The base URL of the server
    public var baseURL: URL? {
        get { return webServer.serverURL }
    }

    // MARK: - Public methods

    public init?() {
        #if DEBUG
            let port = 8080

            GCDWebServer.setLogLevel(2)
        #else
            // Default: random port
            let port = 0

            GCDWebServer.setLogLevel(3)
        #endif
        webServer = GCDWebServer()
        do {
            // TODO: Check if we can use unix socket instead of tcp.
            //       Check if it's supported by WKWebView first.
            try webServer.start(options: [GCDWebServerOption_Port: port,
                                          GCDWebServerOption_BindToLocalhost: true])
        } catch {
            log(level: .error, "Failed to start the HTTP server.")
            logValue(level: .error, error)
            return nil
        }
    }

    // TODO: Github issue #3
    /// Add an EPUB container to the list of EPUBS being served.
    ///
    /// - Parameters:
    ///   - container: The EPUB container of the publication
    ///   - endpoint: The URI prefix to use to fetch assets from the publication
    ///               `/{prefix}/{assetRelativePath}`
    /// - Throws: `EpubServerError.epubParser`
    public func addEpub(container: inout Container, withEndpoint endpoint: String) throws {
        let publication: Publication
        let fetcher: EpubFetcher

        guard containers[endpoint] == nil else {
            log(level: .warning, "\(endpoint) is already in use.")
            return
        }
        // Publication initialisation
        do {
            publication = try parser.parse(container: &container)
        } catch {
            log(level: .error, "The publication parsing failed.")
            throw EpubServerError.epubParser(underlayingError: error)
        }
        addSelfLinkTo(publication: publication, endpoint: endpoint)
        // FIXME: Are these dictionaries really necessary?
        containers[endpoint] = container
        publications[endpoint] = publication
        // Initialize the Fetcher
        do {
            fetcher = try EpubFetcher(publication: publication, container: container)
        } catch {
            log(level: .error, "Fetcher initialisation failed.")
            throw EpubServerError.epubFetcher(underlayingError: error)
        }

        // TODO: Change the handlers so that they avec generic (instead of X 
        //       handlers pet Epub.
        /// Webserver HTTP GET ressources request handler
        func ressourcesHandler(request: GCDWebServerRequest?) -> GCDWebServerResponse? {
            let response: GCDWebServerResponse

            guard let request = request else {
                log(level: .error, "The request received is nil.")
                return GCDWebServerErrorResponse(statusCode: 500)
            }
            guard let path = request.path else {
                log(level: .error, "The request's path to the ressource is empty.")
                return GCDWebServerErrorResponse(statusCode: 500)
            }
            logValue(level: .debug, path)
            // Remove the prefix from the URI
            let relativePath = path.substring(from: path.index(endpoint.endIndex, offsetBy: 3))
            let resource = publication.resource(withRelativePath: relativePath)
            let contentType = resource?.typeLink ?? "application/octet-stream"<commit_msg>Fixed typo : underlaying -> underlying<commit_after>//
//  EpubServer.swift
//  R2Streamer
//
//  Created by <NAME> on 21/12/2016.
//  Copyright © 2016 Readium. All rights reserved.
//

import Foundation
import GCDWebServers

extension EpubServer: Loggable {}

/// Errors thrown by the `EpubServer`.
///
/// - epubParser: An error thrown by the EpubParser.
/// - epubFetcher: An error thrown by the EpubFetcher.
public enum EpubServerError: Error{
    case epubParser(underlyingError: Error)
    case epubFetcher(underlyingError: Error)
}

/// The HTTP server for the publication's manifests and assets. Serves Epubs.
open class EpubServer {

    /// The HTTP server
    var webServer: GCDWebServer

    let parser = EpubParser()

    // FIXME: probably get rid of the server serving multiple epub at a given time
    //          better to implement indexing for multibook search etc
    /// The dictionary of EPUB containers keyed by prefix.
    var containers: [String: Container] = [:]

    /// The dictionaty of publications keyed by prefix.
    var publications: [String: Publication] = [:]

    /// The running HTTP server listening port.
    public var port: UInt? {
        get { return webServer.port }
    }

    /// The base URL of the server
    public var baseURL: URL? {
        get { return webServer.serverURL }
    }

    // MARK: - Public methods

    public init?() {
        #if DEBUG
            let port = 8080

            GCDWebServer.setLogLevel(2)
        #else
            // Default: random port
            let port = 0

            GCDWebServer.setLogLevel(3)
        #endif
        webServer = GCDWebServer()
        do {
            // TODO: Check if we can use unix socket instead of tcp.
            //       Check if it's supported by WKWebView first.
            try webServer.start(options: [GCDWebServerOption_Port: port,
                                          GCDWebServerOption_BindToLocalhost: true])
        } catch {
            log(level: .error, "Failed to start the HTTP server.")
            logValue(level: .error, error)
            return nil
        }
    }

    // TODO: Github issue #3
    /// Add an EPUB container to the list of EPUBS being served.
    ///
    /// - Parameters:
    ///   - container: The EPUB container of the publication
    ///   - endpoint: The URI prefix to use to fetch assets from the publication
    ///               `/{prefix}/{assetRelativePath}`
    /// - Throws: `EpubServerError.epubParser`
    public func addEpub(container: inout Container, withEndpoint endpoint: String) throws {
        let publication: Publication
        let fetcher: EpubFetcher

        guard containers[endpoint] == nil else {
            log(level: .warning, "\(endpoint) is already in use.")
            return
        }
        // Publication initialisation
        do {
            publication = try parser.parse(container: &container)
        } catch {
            log(level: .error, "The publication parsing failed.")
            throw EpubServerError.epubParser(underlyingError: error)
        }
        addSelfLinkTo(publication: publication, endpoint: endpoint)
        // FIXME: Are these dictionaries really necessary?
        containers[endpoint] = container
        publications[endpoint] = publication
        // Initialize the Fetcher
        do {
            fetcher = try EpubFetcher(publication: publication, container: container)
        } catch {
            log(level: .error, "Fetcher initialisation failed.")
            throw EpubServerError.epubFetcher(underlyingError: error)
        }

        // TODO: Change the handlers so that they avec generic (instead of X 
        //       handlers pet Epub.
        /// Webserver HTTP GET ressources request handler
        func ressourcesHandler(request: GCDWebServerRequest?) -> GCDWebServerResponse? {
            let response: GCDWebServerResponse

            guard let request = request else {
                log(level: .error, "The request received is nil.")
                return GCDWebServerErrorResponse(statusCode: 500)
            }
            guard let path = request.path else {
                log(level: .error, "The request's path to the ressource is empty.")
                return GCDWebServerErrorResponse(statusCode: 500)
            }
            logValue(level: .debug, path)
            // Remove the prefix from the URI
            let relativePath = path.substring(from: path.index(endpoint.endIndex, offsetBy: 3))
            let resource = publication.resource(withRelativePath: relativePath)
            let contentType = resource?.typeLink ?? "application/octet-stream"
{'fraction_non_alphanumeric': 0.08615284834071993, 'fraction_numerical': 0.006053399632472165, 'mean_word_length': 2.038423645320197, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2206501', 'n_tokens_mistral': 2471, 'n_tokens_neox': 2388, 'n_words': 812}