# frozen_string_literal: true require 'webrick' require_relative 'logger' require_relative 'core/locales' require_relative 'core/haml_wrapper' require_relative 'core/builder' require_relative 'core/servlet' require_relative 'core/version' require_relative '../core_extensions' # The main Intranet namespace. module Intranet # The core of the Intranet. class Core # The port currently used by the Intranet. # @return [Integer] attr_reader :port # Initializes a new Intranet core instance. The first available port will be used, starting at # +preferred_port+. If +preferred_port+ port is 80 and the user has not enough priviledges to # use that port, port 8080 (or one of the following ports) will be used. # @param logger [Object] The logger. # @param preferred_port [Integer] The preferred port for the web server. def initialize(logger, preferred_port = 80) @logger = logger # Initialize translation module Intranet::Core::Locales.initialize # Initialize HAML wrapper Intranet::Core::HamlWrapper.initialize # Instanciate Intranet Builder and register page builders @builder = Intranet::Core::Builder.new(@logger) # Instanciate WebRick HTTP server @server = load_http_server(preferred_port) www_dir = File.join(__dir__, 'resources', 'www') mount_default_servlets(www_dir) end # Registers a new module to the core. The server must not be running. The module will then be # accessible through the given +path+ under the web server root. # @param responder [Intranet::AbstractResponder] The module responder instance. # @param path [Array] The path, relative to the web server root, representing the module root # directory. The path cannot be empty and have more than 2 elements. Each # element must only contain the following characters: +-_a-z0-9+. # @param resources_dir [String] The path to the directory that contains additional resources # required by the module. This directory should contain three # subdirectories: +haml/+, +locales/+ and +www/+. The +www/+ is # mounted under +/design+ on the web server. # @raise [ArgumentError] If the +path+ is not valid. # @raise [Errno::EALREADY] If the server is already running. def register_module(responder, path, resources_dir = responder.resources_dir) raise Errno::EALREADY if @server.status != :Stop @builder.register(responder, path) module_add_servlet(path.empty? ? ['home'] : path, resources_dir) module_add_locales(resources_dir) module_add_haml_templates(resources_dir) end # Defines the URL of the home page. # @param url [String] The absolute URL of the home page. # @raise [ArgumentError] If the URL is not an absolute path. def home_url=(url) @builder.home_url = url end # Starts the web server. def start @logger.info('Intranet::Core: using locale \'' + I18n.default_locale.to_s + '\'') @logger.info('Intranet::Core: running Intranet version ' + VERSION) # Start serving HTTP requests @server.start end # Stops the web server and finalizes all registered module responders. def stop @logger.info('Intranet::Runner: requesting system shutdown...') @server.shutdown @builder.finalize @logger.close end private # See https://github.com/nahi/webrick/blob/master/lib/webrick/accesslog.rb#L69 ACCESSLOG_FMT = "%h '%r' -> %s (%b bytes in %Ts)" def load_http_server(preferred_port) @port = preferred_port begin WEBrick::HTTPServer.new(Port: @port, Logger: @logger, AccessLog: [[@logger, ACCESSLOG_FMT]]) rescue Errno::EACCES # not enough permission to use port 80 @port = 8080 retry rescue Errno::EADDRINUSE @port += 1 retry end end def mount_default_servlets(www_dir) # Configure handlers for HTTP server # Start serving www/ as HTTP server root "/" using the class WEBrick::HTTPServlet::FileHandler # You can write your own servlet (it must inherit from WEBrick::HTTPServlet::AbstractServlet) # http://ruby-doc.org/stdlib-1.9.3/libdoc/webrick/rdoc/WEBrick/HTTPServlet/AbstractServlet.html # https://www.igvita.com/2007/02/13/building-dynamic-webrick-servers-in-ruby/ @server.mount('/design', WEBrick::HTTPServlet::FileHandler, www_dir) @server.mount('/', Intranet::Core::Servlet, @builder) end def module_add_servlet(path, resources_dir) @server.mount('/' + path.join('/') + '/design', WEBrick::HTTPServlet::FileHandler, File.join(resources_dir, 'www')) end def module_add_locales(resources_dir) Intranet::Core::Locales.add_path(File.join(resources_dir, 'locales')) end def module_add_haml_templates(resources_dir) Intranet::Core::HamlWrapper.add_path(File.join(resources_dir, 'haml')) end end end