Fix #3791/#3478
* Add support for SSL through command line switches. * Add suppport for file/index.html > file.html > directory. * Add support for custom-headers through configuration. * Modernize and split up the serve. * Add a few basic tests.
This commit is contained in:
		
							parent
							
								
									d10dc01290
								
							
						
					
					
						commit
						8efbdc01ff
					
				| 
						 | 
				
			
			@ -1,161 +1,196 @@
 | 
			
		|||
# -*- encoding: utf-8 -*-
 | 
			
		||||
module Jekyll
 | 
			
		||||
  module Commands
 | 
			
		||||
    class Serve < Command
 | 
			
		||||
 | 
			
		||||
      class << self
 | 
			
		||||
        COMMAND_OPTIONS = {
 | 
			
		||||
          "ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."],
 | 
			
		||||
          "host"     => ["host", "-H", "--host [HOST]", "Host to bind to"],
 | 
			
		||||
          "open_url" => ["-o", "--open-url", "Launch your browser with your site."],
 | 
			
		||||
          "detach"   => ["-B", "--detach", "Run the server in the background (detach)"],
 | 
			
		||||
          "ssl_key"  => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."],
 | 
			
		||||
          "port"     => ["-P", "--port [PORT]", "Port to listen on"],
 | 
			
		||||
          "baseurl"  => ["-b", "--baseurl [URL]", "Base URL"],
 | 
			
		||||
          "skip_initial_build" => ["skip_initial_build", "--skip-initial-build",
 | 
			
		||||
            "Skips the initial site build which occurs before the server is started."]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        def init_with_program(prog)
 | 
			
		||||
          prog.command(:serve) do |c|
 | 
			
		||||
            c.syntax 'serve [options]'
 | 
			
		||||
            c.description 'Serve your site locally'
 | 
			
		||||
            c.alias :server
 | 
			
		||||
            c.alias :s
 | 
			
		||||
          prog.command(:serve) do |cmd|
 | 
			
		||||
            cmd.description "Serve your site locally"
 | 
			
		||||
            cmd.syntax "serve [options]"
 | 
			
		||||
            cmd.alias :server
 | 
			
		||||
            cmd.alias :s
 | 
			
		||||
 | 
			
		||||
            add_build_options(c)
 | 
			
		||||
            add_build_options(cmd)
 | 
			
		||||
            COMMAND_OPTIONS.each do |key, val|
 | 
			
		||||
              cmd.option key, *val
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            c.option 'detach', '-B', '--detach', 'Run the server in the background (detach)'
 | 
			
		||||
            c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
 | 
			
		||||
            c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
 | 
			
		||||
            c.option 'baseurl', '-b', '--baseurl [URL]', 'Base URL'
 | 
			
		||||
            c.option 'skip_initial_build', '--skip-initial-build', 'Skips the initial site build which occurs before the server is started.'
 | 
			
		||||
            c.option 'open_url', '-o', '--open-url', 'Opens the local URL in your default browser'
 | 
			
		||||
 | 
			
		||||
            c.action do |args, options|
 | 
			
		||||
              options["serving"] = true
 | 
			
		||||
              options["watch"] = true unless options.key?("watch")
 | 
			
		||||
              Jekyll::Commands::Build.process(options)
 | 
			
		||||
              Jekyll::Commands::Serve.process(options)
 | 
			
		||||
            cmd.action do |_, opts|
 | 
			
		||||
              opts["serving"] = true
 | 
			
		||||
              opts["watch"  ] = true unless opts.key?("watch")
 | 
			
		||||
              Build.process(opts)
 | 
			
		||||
              Serve.process(opts)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Boot up a WEBrick server which points to the compiled site's root.
 | 
			
		||||
        def process(options)
 | 
			
		||||
          options = configuration_from_options(options)
 | 
			
		||||
          destination = options['destination']
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        def process(opts)
 | 
			
		||||
          opts = configuration_from_options(opts)
 | 
			
		||||
          destination = opts["destination"]
 | 
			
		||||
          setup(destination)
 | 
			
		||||
 | 
			
		||||
          s = WEBrick::HTTPServer.new(webrick_options(options))
 | 
			
		||||
          s.unmount("")
 | 
			
		||||
          server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
 | 
			
		||||
          server.mount(opts["baseurl"], Servlet, destination, file_handler_opts)
 | 
			
		||||
          Jekyll.logger.info "Server address:", server_address(server, opts)
 | 
			
		||||
          launch_browser server, opts if opts["open_url"]
 | 
			
		||||
          boot_or_detach server, opts
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
          s.mount(
 | 
			
		||||
            options['baseurl'],
 | 
			
		||||
            custom_file_handler,
 | 
			
		||||
            destination,
 | 
			
		||||
            file_handler_options
 | 
			
		||||
          )
 | 
			
		||||
        # Do a base pre-setup of WEBRick so that everything is in place
 | 
			
		||||
        # when we get ready to party, checking for an setting up an error page
 | 
			
		||||
        # and making sure our destination exists.
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def setup(destination)
 | 
			
		||||
          require_relative "serve/servlet"
 | 
			
		||||
 | 
			
		||||
          server_address_str = server_address(s, options)
 | 
			
		||||
          Jekyll.logger.info "Server address:", server_address_str
 | 
			
		||||
 | 
			
		||||
          if options["open_url"]
 | 
			
		||||
            command = Utils::Platforms.windows?? "start" : Utils::Platforms.osx?? \
 | 
			
		||||
              "open" : "xdg-open"
 | 
			
		||||
              
 | 
			
		||||
            system command, server_address_str
 | 
			
		||||
          FileUtils.mkdir_p(destination)
 | 
			
		||||
          if File.exist?(File.join(destination, "404.html"))
 | 
			
		||||
            WEBrick::HTTPResponse.class_eval do
 | 
			
		||||
              def create_error_page
 | 
			
		||||
                @header["Content-Type"] = "text/html; charset=UTF-8"
 | 
			
		||||
                @body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def webrick_opts(opts)
 | 
			
		||||
          opts = {
 | 
			
		||||
            :JekyllOptions      => opts,
 | 
			
		||||
            :DoNotReverseLookup => true,
 | 
			
		||||
            :MimeTypes          => mime_types,
 | 
			
		||||
            :DocumentRoot       => opts["destination"],
 | 
			
		||||
            :StartCallback      => start_callback(opts["detach"]),
 | 
			
		||||
            :BindAddress        => opts["host"],
 | 
			
		||||
            :Port               => opts["port"],
 | 
			
		||||
            :DirectoryIndex     => %W(
 | 
			
		||||
              index.htm
 | 
			
		||||
              index.html
 | 
			
		||||
              index.rhtml
 | 
			
		||||
              index.cgi
 | 
			
		||||
              index.xml
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          enable_ssl(opts)
 | 
			
		||||
          enable_logging(opts)
 | 
			
		||||
          opts
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Recreate NondisclosureName under utf-8 circumstance
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def file_handler_opts
 | 
			
		||||
          WEBrick::Config::FileHandler.merge({
 | 
			
		||||
            :FancyIndexing     => true,
 | 
			
		||||
            :NondisclosureName => [
 | 
			
		||||
              '.ht*','~*'
 | 
			
		||||
            ]
 | 
			
		||||
          })
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def server_address(server, opts)
 | 
			
		||||
          address = server.config[:BindAddress]
 | 
			
		||||
          baseurl = "#{opts["baseurl"]}/" if opts["baseurl"]
 | 
			
		||||
          port = server.config[:Port]
 | 
			
		||||
 | 
			
		||||
          "http://#{address}:#{port}#{baseurl}"
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def launch_browser(server, opts)
 | 
			
		||||
          command = Utils::Platforms.windows?? "start" : Utils::Platforms.osx?? "open" : "xdg-open"
 | 
			
		||||
          system command, server_address(server, opts)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Keep in our area with a thread or detach the server as requested
 | 
			
		||||
        # by the user.  This method determines what we do based on what you
 | 
			
		||||
        # ask us to do.
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def boot_or_detach(server, opts)
 | 
			
		||||
          if opts["detach"]
 | 
			
		||||
            pid = Process.fork do
 | 
			
		||||
              server.start
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
          if options['detach'] # detach the server
 | 
			
		||||
            pid = Process.fork { s.start }
 | 
			
		||||
            Process.detach(pid)
 | 
			
		||||
            Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
 | 
			
		||||
          else # create a new server thread, then join it with current terminal
 | 
			
		||||
            t = Thread.new { s.start }
 | 
			
		||||
            trap("INT") { s.shutdown }
 | 
			
		||||
            Jekyll.logger.info "Server detached with pid '#{pid}'.", \
 | 
			
		||||
              "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
 | 
			
		||||
          else
 | 
			
		||||
            t = Thread.new { server.start }
 | 
			
		||||
            trap("INT") { server.shutdown }
 | 
			
		||||
            t.join
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def setup(destination)
 | 
			
		||||
          require 'webrick'
 | 
			
		||||
        # Make the stack verbose if the user requests it.
 | 
			
		||||
 | 
			
		||||
          FileUtils.mkdir_p(destination)
 | 
			
		||||
 | 
			
		||||
          # monkey patch WEBrick using custom 404 page (/404.html)
 | 
			
		||||
          if File.exist?(File.join(destination, '404.html'))
 | 
			
		||||
            WEBrick::HTTPResponse.class_eval do
 | 
			
		||||
              def create_error_page
 | 
			
		||||
                @header['content-type'] = "text/html; charset=UTF-8"
 | 
			
		||||
                @body = IO.read(File.join(@config[:DocumentRoot], '404.html'))
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        private
 | 
			
		||||
        def enable_logging(opts)
 | 
			
		||||
          opts[:AccessLog] = []
 | 
			
		||||
          level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
 | 
			
		||||
          opts[:Logger] = WEBrick::Log.new($stdout, level)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def webrick_options(config)
 | 
			
		||||
          opts = {
 | 
			
		||||
            :BindAddress        => config['host'],
 | 
			
		||||
            :DirectoryIndex     => %w(index.html index.htm index.cgi index.rhtml index.xml),
 | 
			
		||||
            :DocumentRoot       => config['destination'],
 | 
			
		||||
            :DoNotReverseLookup => true,
 | 
			
		||||
            :MimeTypes          => mime_types,
 | 
			
		||||
            :Port               => config['port'],
 | 
			
		||||
            :StartCallback      => start_callback(config['detach'])
 | 
			
		||||
          }
 | 
			
		||||
        # Add SSL to the stack if the user triggers --enable-ssl and they
 | 
			
		||||
        # provide both types of certificates commonly needed.  Raise if they
 | 
			
		||||
        # forget to add one of the certificates.
 | 
			
		||||
 | 
			
		||||
          if config['verbose']
 | 
			
		||||
            opts.merge!({
 | 
			
		||||
              :Logger => WEBrick::Log.new($stdout, WEBrick::Log::DEBUG)
 | 
			
		||||
            })
 | 
			
		||||
          else
 | 
			
		||||
            opts.merge!({
 | 
			
		||||
              :AccessLog => [],
 | 
			
		||||
              :Logger => WEBrick::Log.new([], WEBrick::Log::WARN)
 | 
			
		||||
            })
 | 
			
		||||
        private
 | 
			
		||||
        def enable_ssl(opts)
 | 
			
		||||
          return if !opts[:JekyllOptions]["ssl_cert"] && !opts[:JekyllOptions]["ssl_key"]
 | 
			
		||||
          if !opts[:JekyllOptions]["ssl_cert"] || !opts[:JekyllOptions]["ssl_key"]
 | 
			
		||||
            raise RuntimeError, "--ssl-cert or --ssl-key missing."
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          opts
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Custom WEBrick FileHandler servlet for serving "/file.html" at "/file"
 | 
			
		||||
        # when no exact match is found. This mirrors the behavior of GitHub
 | 
			
		||||
        # Pages and many static web server configs.
 | 
			
		||||
        def custom_file_handler
 | 
			
		||||
          Class.new WEBrick::HTTPServlet::FileHandler do
 | 
			
		||||
            def search_file(req, res, basename)
 | 
			
		||||
              if file = super
 | 
			
		||||
                file
 | 
			
		||||
              else
 | 
			
		||||
                super(req, res, "#{basename}.html")
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
          require "openssl"; require "webrick/https"
 | 
			
		||||
          source_key = Jekyll.sanitized_path(opts[:JekyllOptions]["source"], opts[:JekyllOptions]["ssl_key" ])
 | 
			
		||||
          source_certificate = Jekyll.sanitized_path(opts[:JekyllOptions]["source"], opts[:JekyllOptions]["ssl_cert"])
 | 
			
		||||
          opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(source_certificate))
 | 
			
		||||
          opts[:SSLPrivateKey ] = OpenSSL::PKey::RSA.new(File.read(source_key))
 | 
			
		||||
          opts[:EnableSSL] = true
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def start_callback(detached)
 | 
			
		||||
          unless detached
 | 
			
		||||
            Proc.new { Jekyll.logger.info "Server running...", "press ctrl-c to stop." }
 | 
			
		||||
            proc do
 | 
			
		||||
              Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def mime_types
 | 
			
		||||
          mime_types_file = File.expand_path('../mime.types', File.dirname(__FILE__))
 | 
			
		||||
          WEBrick::HTTPUtils::load_mime_types(mime_types_file)
 | 
			
		||||
          file = File.expand_path('../mime.types', File.dirname(__FILE__))
 | 
			
		||||
          WEBrick::HTTPUtils.load_mime_types(file)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def server_address(server, options)
 | 
			
		||||
          baseurl = "#{options['baseurl']}/" if options['baseurl']
 | 
			
		||||
          [
 | 
			
		||||
            "http://",
 | 
			
		||||
            server.config[:BindAddress],
 | 
			
		||||
            ":",
 | 
			
		||||
            server.config[:Port],
 | 
			
		||||
            baseurl || ""
 | 
			
		||||
          ].map(&:to_s).join("")
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # recreate NondisclosureName under utf-8 circumstance
 | 
			
		||||
        def file_handler_options
 | 
			
		||||
          WEBrick::Config::FileHandler.merge({
 | 
			
		||||
            :FancyIndexing     => true,
 | 
			
		||||
            :NondisclosureName => ['.ht*','~*']
 | 
			
		||||
          })
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
require "webrick"
 | 
			
		||||
 | 
			
		||||
module Jekyll
 | 
			
		||||
  module Commands
 | 
			
		||||
    class Serve
 | 
			
		||||
      class Servlet < WEBrick::HTTPServlet::FileHandler
 | 
			
		||||
        HEADER_DEFAULTS = {}
 | 
			
		||||
 | 
			
		||||
        def initialize(server, root, callbacks)
 | 
			
		||||
          extract_headers(server.config[:JekyllOptions])
 | 
			
		||||
          super
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
 | 
			
		||||
        def do_GET(req, res)
 | 
			
		||||
          res.header.merge!(@headers) if @headers.any?
 | 
			
		||||
          return super
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # ---------------------------------------------------------------------
 | 
			
		||||
        # file > file/index.html > file.html > directory -> Having a directory
 | 
			
		||||
        # with the same name as a file will result in the file being served the
 | 
			
		||||
        # way that Nginx behaves (probably not exactly...) For browsing.
 | 
			
		||||
        # ---------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
        def search_file(req, res, basename)
 | 
			
		||||
          return file if (file = super) || (file = super req, res, "#{basename}.html")
 | 
			
		||||
 | 
			
		||||
          file = "#{req.path.gsub(/\/\Z/, "")}.html"
 | 
			
		||||
          if file && File.file?(File.join(@config[:DocumentRoot], file))
 | 
			
		||||
            return ".html"
 | 
			
		||||
          end
 | 
			
		||||
        nil
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def extract_headers(opts)
 | 
			
		||||
          @headers = add_defaults(opts.fetch("webrick", {}).fetch("headers", {}))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def add_defaults(opts)
 | 
			
		||||
          control_development_cache(opts)
 | 
			
		||||
          HEADER_DEFAULTS.each_with_object(opts) do |(k, v), h|
 | 
			
		||||
            h[k] = v if !h[k]
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def control_development_cache(opts)
 | 
			
		||||
          if !opts.has_key?("Cache-Control") && Jekyll.env == "development"
 | 
			
		||||
            opts["Cache-Control"] = "private, max-age=0, proxy-revalidate, no-store, no-cache, must-revalidate"
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -352,6 +352,24 @@ before your site is served.
 | 
			
		|||
        <p><code class="flag">--skip-initial-build</code></p>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr class="setting">
 | 
			
		||||
      <td>
 | 
			
		||||
        <p class="name"><strong>X.509 (SSL) Private Key</strong></p>
 | 
			
		||||
        <p class="description">SSL Private Key.</p>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td class="align-center">
 | 
			
		||||
        <p><code class="flag">--ssl-key</code></p>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr class="setting">
 | 
			
		||||
      <td>
 | 
			
		||||
        <p class="name"><strong>X.509 (SSL) Certificate</strong></p>
 | 
			
		||||
        <p class="description">SSL Public certificate.</p>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td class="align-center">
 | 
			
		||||
        <p><code class="flag">--ssl-cert</code></p>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
  </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -364,6 +382,24 @@ before your site is served.
 | 
			
		|||
  </p>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
## Custom WEBRick Headers
 | 
			
		||||
 | 
			
		||||
You can provide custom headers for your site by adding them to `_config.yml`
 | 
			
		||||
 | 
			
		||||
{% highlight yaml %}
 | 
			
		||||
# File: _config.yml
 | 
			
		||||
webrick:
 | 
			
		||||
  headers:
 | 
			
		||||
    My-Header: My-Value
 | 
			
		||||
    My-Other-Header: My-Other-Value
 | 
			
		||||
{% endhighlight %}
 | 
			
		||||
 | 
			
		||||
### Defaults
 | 
			
		||||
 | 
			
		||||
We only provide on default and that's a Content-Type header that disables
 | 
			
		||||
caching in development so that you don't have to fight with Chrome's aggressive
 | 
			
		||||
caching when you are in development mode.
 | 
			
		||||
 | 
			
		||||
## Specifying a Jekyll environment at build time
 | 
			
		||||
 | 
			
		||||
In the build (or serve) arguments, you can specify a Jekyll environment and value. The build will then apply this value in any conditional statements in your content.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
require "webrick"
 | 
			
		||||
require "mercenary"
 | 
			
		||||
require "helper"
 | 
			
		||||
 | 
			
		||||
class TestCommandsServe < JekyllUnitTest
 | 
			
		||||
  def custom_opts(what)
 | 
			
		||||
    @cmd.send(
 | 
			
		||||
      :webrick_opts, what
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context "with a program" do
 | 
			
		||||
    setup do
 | 
			
		||||
      @merc, @cmd = nil, Jekyll::Commands::Serve
 | 
			
		||||
      Mercenary.program(:jekyll) do |p|
 | 
			
		||||
        @merc = @cmd.init_with_program(
 | 
			
		||||
          p
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    should "label itself" do
 | 
			
		||||
      assert_equal(
 | 
			
		||||
        @merc.name, :serve
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    should "have aliases" do
 | 
			
		||||
      assert_includes @merc.aliases, :s
 | 
			
		||||
      assert_includes @merc.aliases, :server
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    should "have a description" do
 | 
			
		||||
      refute_nil(
 | 
			
		||||
        @merc.description
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    should "have an action" do
 | 
			
		||||
      refute_empty(
 | 
			
		||||
        @merc.actions
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    should "not have an empty options set" do
 | 
			
		||||
      refute_empty(
 | 
			
		||||
        @merc.options
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "with custom options" do
 | 
			
		||||
      should "create a default set of mimetypes" do
 | 
			
		||||
        refute_nil custom_opts({})[
 | 
			
		||||
          :MimeTypes
 | 
			
		||||
        ]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      should "use user destinations" do
 | 
			
		||||
        assert_equal "foo", custom_opts({ "destination" => "foo" })[
 | 
			
		||||
          :DocumentRoot
 | 
			
		||||
        ]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      should "use user port" do
 | 
			
		||||
        # WHAT?!?!1 Over 9000? That's impossible.
 | 
			
		||||
        assert_equal 9001, custom_opts( { "port" => 9001 })[
 | 
			
		||||
          :Port
 | 
			
		||||
        ]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context "verbose" do
 | 
			
		||||
        should "debug when verbose" do
 | 
			
		||||
          assert_equal custom_opts({ "verbose" => true })[:Logger].level, 5
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        should "warn when not verbose" do
 | 
			
		||||
          assert_equal custom_opts({})[:Logger].level, 3
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context "enabling ssl" do
 | 
			
		||||
        should "raise if enabling without key or cert" do
 | 
			
		||||
          assert_raises RuntimeError do
 | 
			
		||||
            custom_opts({
 | 
			
		||||
              "ssl_key" => "foo"
 | 
			
		||||
            })
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          assert_raises RuntimeError do
 | 
			
		||||
            custom_opts({
 | 
			
		||||
              "ssl_key" => "foo"
 | 
			
		||||
            })
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        should "allow SSL with a key and cert" do
 | 
			
		||||
          expect(OpenSSL::PKey::RSA).to receive(:new).and_return("c2")
 | 
			
		||||
          expect(OpenSSL::X509::Certificate).to receive(:new).and_return("c1")
 | 
			
		||||
          allow(File).to receive(:read).and_return("foo")
 | 
			
		||||
 | 
			
		||||
          result = custom_opts({
 | 
			
		||||
            "ssl_cert" => "foo",
 | 
			
		||||
            "source" => "bar",
 | 
			
		||||
            "enable_ssl" => true,
 | 
			
		||||
            "ssl_key" => "bar"
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
          assert result[:EnableSSL]
 | 
			
		||||
          assert_equal result[:SSLPrivateKey ], "c2"
 | 
			
		||||
          assert_equal result[:SSLCertificate], "c1"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
		Reference in New Issue