about summary refs log tree commit homepage
path: root/lib/yahns/rack.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/yahns/rack.rb')
-rw-r--r--lib/yahns/rack.rb80
1 files changed, 80 insertions, 0 deletions
diff --git a/lib/yahns/rack.rb b/lib/yahns/rack.rb
new file mode 100644
index 0000000..27857ec
--- /dev/null
+++ b/lib/yahns/rack.rb
@@ -0,0 +1,80 @@
+# -*- encoding: binary -*-
+# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require 'rack'
+class Yahns::Rack # :nodoc:
+  attr_reader :preload
+
+  # enforce a single instance for the identical config.ru
+  def self.instance_key(*args)
+    ru = args[0]
+
+    # it's safe to expand_path now since we enforce working_directory in the
+    # top-level config is called before any apps are created
+    # ru may also be a Rack::Builder object or any already-built Rack app
+    ru.respond_to?(:call) ? ru.object_id : File.expand_path(ru)
+  end
+
+  def initialize(ru, opts = {})
+    # always called after config file parsing, may be called after forking
+    @app = lambda do
+      ENV["RACK_ENV"] ||= "none"
+      if ru.respond_to?(:call)
+        inner_app = ru.respond_to?(:to_app) ? ru.to_app : ru
+      else
+        inner_app = case ru
+        when /\.ru$/
+          raw = File.read(ru)
+          raw.sub!(/^__END__\n.*/, '')
+          eval("Rack::Builder.new {(\n#{raw}\n)}.to_app", TOPLEVEL_BINDING, ru)
+        else
+          require ru
+          Object.const_get(File.basename(ru, '.rb').capitalize)
+        end
+      end
+      inner_app
+    end
+    @ru = ru
+    @preload = opts[:preload]
+    build_app! if @preload
+  end
+
+  def config_context
+    ctx_class = Class.new(Yahns::HttpClient)
+    ctx_class.extend(Yahns::HttpContext)
+    ctx_class.http_ctx_init(self)
+    ctx_class
+  end
+
+  def build_app!
+    if @app.respond_to?(:arity) && @app.arity == 0
+      Gem.refresh if defined?(Gem) && Gem.respond_to?(:refresh)
+      @app = @app.call
+    end
+  end
+
+  # allow different HttpContext instances to have different Rack defaults
+  def app_defaults
+    {
+      # logger is set in http_context
+      "rack.errors" => $stderr,
+      "rack.multiprocess" => true,
+      "rack.multithread" => true,
+      "rack.run_once" => false,
+      "rack.hijack?" => true,
+      "rack.version" => [ 1, 2 ],
+      "SCRIPT_NAME" => "",
+
+      # this is not in the Rack spec, but some apps may rely on it
+      "SERVER_SOFTWARE" => "yahns"
+    }
+  end
+
+  def app_after_fork
+    build_app! unless @preload
+    @app
+  end
+end
+
+# register ourselves
+Yahns::Config::APP_CLASS[:rack] = Yahns::Rack