summaryrefslogtreecommitdiff
path: root/lib/rack/handler.rb
blob: 22d547453540088e0e9069de8403d34f047597b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# frozen_string_literal: true

module Rack
  # *Handlers* connect web servers with Rack.
  #
  # Rack includes Handlers for Thin, WEBrick, FastCGI, CGI, SCGI
  # and LiteSpeed.
  #
  # Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
  # A second optional hash can be passed to include server-specific
  # configuration.
  module Handler
    def self.get(server)
      return unless server
      server = server.to_s

      unless @handlers.include? server
        load_error = try_require('rack/handler', server)
      end

      if klass = @handlers[server]
        const_get(klass)
      else
        const_get(server, false)
      end

    rescue NameError => name_error
      raise load_error || name_error
    end

    # Select first available Rack handler given an `Array` of server names.
    # Raises `LoadError` if no handler was found.
    #
    #   > pick ['puma', 'webrick']
    #   => Rack::Handler::WEBrick
    def self.pick(server_names)
      server_names = Array(server_names)
      server_names.each do |server_name|
        begin
          return get(server_name.to_s)
        rescue LoadError, NameError
        end
      end

      raise LoadError, "Couldn't find handler for: #{server_names.join(', ')}."
    end

    RACK_HANDLER = 'RACK_HANDLER'

    SERVER_NAMES = %w(puma falcon webrick).freeze
    private_constant :SERVER_NAMES

    def self.default
      if rack_handler = ENV[RACK_HANDLER]
        self.get(rack_handler)
      else
        pick SERVER_NAMES
      end
    end

    # Transforms server-name constants to their canonical form as filenames,
    # then tries to require them but silences the LoadError if not found
    #
    # Naming convention:
    #
    #   Foo # => 'foo'
    #   FooBar # => 'foo_bar.rb'
    #   FooBAR # => 'foobar.rb'
    #   FOObar # => 'foobar.rb'
    #   FOOBAR # => 'foobar.rb'
    #   FooBarBaz # => 'foo_bar_baz.rb'
    def self.try_require(prefix, const_name)
      file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }.
        gsub(/[A-Z]+[^A-Z]/, '_\&').downcase

      require(::File.join(prefix, file))
      nil
    rescue LoadError => error
      error
    end

    def self.register(server, klass)
      @handlers ||= {}
      @handlers[server.to_s] = klass.to_s
    end

    autoload :WEBrick, "rack/handler/webrick"
    register 'webrick', 'Rack::Handler::WEBrick'
  end
end