Skip to content

Instantly share code, notes, and snippets.

@kuboon
Created February 8, 2023 04:09
Show Gist options
  • Select an option

  • Save kuboon/b3df5ff2002b8bcd15ee26a680d7cb02 to your computer and use it in GitHub Desktop.

Select an option

Save kuboon/b3df5ff2002b8bcd15ee26a680d7cb02 to your computer and use it in GitHub Desktop.

Revisions

  1. kuboon created this gist Feb 8, 2023.
    3 changes: 3 additions & 0 deletions .htaccess
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ dispatch.cgi/$1 [QSA,L]
    125 changes: 125 additions & 0 deletions dispatch.cgi
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    #!/bin/ruby
    require 'bundler/setup' # Set up gems listed in the Gemfile.
    require 'drb/drb'
    require 'logger'
    require "rack"
    require 'rack/rewindable_input'

    module Dispatch
    RAILS_ENV = 'value_server'
    DRUBY_URI = "druby://localhost:13142"
    LOCKFILE = "../tmp/pids/dispatch.pid"
    LOGGER = Logger.new('../log/dispatch.log')

    class CGIHandler
    def initialize(app)
    @app = app
    end
    def call(env, stdin, stdout)
    env.update(Rack::SCRIPT_NAME => '', # clear
    "rack.version" => Rack::VERSION,
    "rack.input" => stdin,
    "rack.multithread" => false,
    "rack.multiprocess" => true,
    "rack.run_once" => true,
    "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
    )

    LOGGER.info "#{DRb.uri} call rack"
    status, headers, body = @app.call(env)
    LOGGER.info "#{DRb.uri} status: #{status}"
    begin
    stdout.print "Status: #{status}\r\n"
    send_headers stdout, headers
    send_body stdout, body
    ensure
    body.close if body.respond_to? :close
    end
    end

    private

    def send_headers(stdout, headers)
    headers.each { |k, vs|
    vs.split("\n").each { |v|
    stdout.print "#{k}: #{v}\r\n"
    }
    }
    stdout.print "\r\n"
    stdout.flush
    end

    def send_body(stdout, body)
    body.each { |part|
    stdout.print part.to_str
    stdout.flush
    }
    end
    end

    class << self
    def main
    spawn_server unless server_alive?

    $stdin.binmode

    DRb.start_service
    cgi_handler = DRbObject.new_with_uri(DRUBY_URI)

    LOGGER.info "#{DRb.uri} call cgi_handler"
    cgi_handler.call(ENV.to_h, Rack::RewindableInput.new($stdin), $stdout)
    LOGGER.info "#{DRb.uri} done"
    rescue => e
    puts "Content-Type: text/plain\nStatus: 500\n\n#{e}"
    puts e.backtrace
    LOGGER.error ([e] + e.backtrace).join("\n")
    end

    private

    def server_alive?
    return false unless File.exist?(LOCKFILE)
    return true if process_alive? File.read(LOCKFILE).to_i

    File.delete(LOCKFILE)
    false
    end

    def process_alive?(pid)
    Process.getpgid( pid )
    true
    rescue Errno::ESRCH
    false
    end

    def spawn_server
    File.open(LOCKFILE, "w") do |f|
    if f.flock(File::LOCK_EX | File::LOCK_NB)
    f.puts fork { start_service }
    sleep 5
    else
    LOGGER.error("lock failed")
    end
    end
    end

    def start_service
    $stdout.reopen("/dev/null", "w")
    $stderr.reopen("/dev/null", "w")
    LOGGER.info 'starting server..'
    DRb.start_service(DRUBY_URI, cgi_handler)
    LOGGER.info 'started server'
    DRb.thread.join
    rescue => e
    LOGGER.error ([e] + e.backtrace).join("\n")
    end

    def cgi_handler
    ENV['RAILS_ENV'] = RAILS_ENV
    require '../config/environment'
    CGIHandler.new(Rails.application)
    end
    end
    end

    Dispatch.main