macournoyer / thin
- Source
- Commits
- Network (36)
- Issues (6)
- Downloads (17)
- Wiki (1)
- Graphs
-
Tree:
1d1faea
commit 1d1faea0401103e2d16cfb38b19d7fd133053cb5
tree fbcf407c7fe50067672e6b993de7a5d2af1a398b
parent 8968de296a0edc8a233ae8d7d2717615f0305815
tree fbcf407c7fe50067672e6b993de7a5d2af1a398b
parent 8968de296a0edc8a233ae8d7d2717615f0305815
| ed73729b » | macournoyer | 2008-01-02 | 1 | require 'cgi' | |
| 2 | |||||
| 3 | # Adapter to run a Rails app with any supported Rack handler. | ||||
| 4 | # By default it will try to load the Rails application in the | ||||
| 5 | # current directory in the development environment. | ||||
| 13bb5800 » | grempe | 2008-03-20 | 6 | # | |
| ed73729b » | macournoyer | 2008-01-02 | 7 | # Options: | |
| 8 | # root: Root directory of the Rails app | ||||
| 13bb5800 » | grempe | 2008-03-20 | 9 | # environment: Rails environment to run in (development [default], production or test) | |
| 10 | # prefix: Set the relative URL root. | ||||
| 11 | # | ||||
| ed73729b » | macournoyer | 2008-01-02 | 12 | # Based on http://fuzed.rubyforge.org/ Rails adapter | |
| 13 | module Rack | ||||
| 14 | module Adapter | ||||
| 15 | class Rails | ||||
| 1d1faea0 » | macournoyer | 2008-04-02 | 16 | FILE_METHODS = %w(GET HEAD).freeze | |
| 17 | |||||
| ed73729b » | macournoyer | 2008-01-02 | 18 | def initialize(options={}) | |
| d0e2e14d » | macournoyer | 2008-01-13 | 19 | @root = options[:root] || Dir.pwd | |
| 20 | @env = options[:environment] || 'development' | ||||
| 21 | @prefix = options[:prefix] | ||||
| ed73729b » | macournoyer | 2008-01-02 | 22 | ||
| 23 | load_application | ||||
| 24 | |||||
| 25 | @file_server = Rack::File.new(::File.join(RAILS_ROOT, "public")) | ||||
| 26 | end | ||||
| 27 | |||||
| 28 | def load_application | ||||
| 29 | ENV['RAILS_ENV'] = @env | ||||
| 30 | |||||
| 31 | require "#{@root}/config/environment" | ||||
| 32 | require 'dispatcher' | ||||
| d0e2e14d » | macournoyer | 2008-01-13 | 33 | ||
| 34 | ActionController::AbstractRequest.relative_url_root = @prefix if @prefix | ||||
| ed73729b » | macournoyer | 2008-01-02 | 35 | end | |
| 36 | |||||
| 37 | # TODO refactor this in File#can_serve?(path) ?? | ||||
| 6b31acff » | macournoyer | 2008-01-06 | 38 | def file_exist?(path) | |
| ed73729b » | macournoyer | 2008-01-02 | 39 | full_path = ::File.join(@file_server.root, Utils.unescape(path)) | |
| 40 | ::File.file?(full_path) && ::File.readable?(full_path) | ||||
| 41 | end | ||||
| 42 | |||||
| 6b31acff » | macournoyer | 2008-01-06 | 43 | def serve_file(env) | |
| 44 | @file_server.call(env) | ||||
| 45 | end | ||||
| 46 | |||||
| 47 | def serve_rails(env) | ||||
| ed73729b » | macournoyer | 2008-01-02 | 48 | request = Request.new(env) | |
| 49 | response = Response.new | ||||
| 50 | |||||
| 51 | session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS | ||||
| 52 | cgi = CGIWrapper.new(request, response) | ||||
| 53 | |||||
| 54 | Dispatcher.dispatch(cgi, session_options, response) | ||||
| 55 | |||||
| 56 | response.finish | ||||
| 57 | end | ||||
| 6b31acff » | macournoyer | 2008-01-06 | 58 | ||
| 59 | def call(env) | ||||
| 60 | path = env['PATH_INFO'].chomp('/') | ||||
| 1d1faea0 » | macournoyer | 2008-04-02 | 61 | method = env['REQUEST_METHOD'] | |
| 6b31acff » | macournoyer | 2008-01-06 | 62 | cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension | |
| 63 | |||||
| 1d1faea0 » | macournoyer | 2008-04-02 | 64 | if FILE_METHODS.include?(method) | |
| 65 | if file_exist?(path) # Serve the file if it's there | ||||
| 66 | return serve_file(env) | ||||
| 67 | elsif file_exist?(cached_path) # Serve the page cache if it's there | ||||
| 68 | env['PATH_INFO'] = cached_path | ||||
| 69 | return serve_file(env) | ||||
| 70 | end | ||||
| 6b31acff » | macournoyer | 2008-01-06 | 71 | end | |
| 1d1faea0 » | macournoyer | 2008-04-02 | 72 | ||
| 73 | # No static file, let Rails handle it | ||||
| 74 | serve_rails(env) | ||||
| 6b31acff » | macournoyer | 2008-01-06 | 75 | end | |
| ed73729b » | macournoyer | 2008-01-02 | 76 | ||
| 77 | protected | ||||
| 78 | |||||
| 79 | class CGIWrapper < ::CGI | ||||
| 80 | def initialize(request, response, *args) | ||||
| 81 | @request = request | ||||
| 82 | @response = response | ||||
| 83 | @args = *args | ||||
| 84 | @input = request.body | ||||
| 85 | |||||
| 86 | super *args | ||||
| 87 | end | ||||
| 88 | |||||
| 89 | def header(options = "text/html") | ||||
| 90 | if options.is_a?(String) | ||||
| 91 | @response['Content-Type'] = options unless @response['Content-Type'] | ||||
| 92 | else | ||||
| 93 | @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length'] | ||||
| 94 | |||||
| 95 | @response['Content-Type'] = options.delete('type') || "text/html" | ||||
| 96 | @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset'] | ||||
| 97 | |||||
| 98 | @response['Content-Language'] = options.delete('language') if options['language'] | ||||
| 99 | @response['Expires'] = options.delete('expires') if options['expires'] | ||||
| 100 | |||||
| 101 | @response.status = options.delete('Status') if options['Status'] | ||||
| cfd4a7ae » | macournoyer | 2008-01-05 | 102 | ||
| ed73729b » | macournoyer | 2008-01-02 | 103 | # Convert 'cookie' header to 'Set-Cookie' headers. | |
| 96ef5249 » | macournoyer | 2008-01-06 | 104 | # Because Set-Cookie header can appear more the once in the response body, | |
| 105 | # we store it in a line break seperated string that will be translated to | ||||
| 106 | # multiple Set-Cookie header by the handler. | ||||
| cfd4a7ae » | macournoyer | 2008-01-05 | 107 | if cookie = options.delete('cookie') | |
| 96ef5249 » | macournoyer | 2008-01-06 | 108 | cookies = [] | |
| cfd4a7ae » | macournoyer | 2008-01-05 | 109 | ||
| 110 | case cookie | ||||
| 96ef5249 » | macournoyer | 2008-01-06 | 111 | when Array then cookie.each { |c| cookies << c.to_s } | |
| 112 | when Hash then cookie.each { |_, c| cookies << c.to_s } | ||||
| 113 | else cookies << cookie.to_s | ||||
| ed73729b » | macournoyer | 2008-01-02 | 114 | end | |
| 115 | |||||
| 96ef5249 » | macournoyer | 2008-01-06 | 116 | @output_cookies.each { |c| cookies << c.to_s } if @output_cookies | |
| 117 | |||||
| 118 | @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n") | ||||
| ed73729b » | macournoyer | 2008-01-02 | 119 | end | |
| cfd4a7ae » | macournoyer | 2008-01-05 | 120 | ||
| 121 | options.each { |k,v| @response[k] = v } | ||||
| ed73729b » | macournoyer | 2008-01-02 | 122 | end | |
| cfd4a7ae » | macournoyer | 2008-01-05 | 123 | ||
| ed73729b » | macournoyer | 2008-01-02 | 124 | "" | |
| 125 | end | ||||
| 126 | |||||
| 127 | def params | ||||
| 128 | @params ||= @request.params | ||||
| 129 | end | ||||
| 130 | |||||
| 131 | def cookies | ||||
| 132 | @request.cookies | ||||
| 133 | end | ||||
| 134 | |||||
| 135 | def query_string | ||||
| 136 | @request.query_string | ||||
| 137 | end | ||||
| 138 | |||||
| 139 | # Used to wrap the normal args variable used inside CGI. | ||||
| 140 | def args | ||||
| 141 | @args | ||||
| 142 | end | ||||
| 143 | |||||
| 144 | # Used to wrap the normal env_table variable used inside CGI. | ||||
| 145 | def env_table | ||||
| 146 | @request.env | ||||
| 147 | end | ||||
| 148 | |||||
| 149 | # Used to wrap the normal stdinput variable used inside CGI. | ||||
| 150 | def stdinput | ||||
| 151 | @input | ||||
| 152 | end | ||||
| 153 | |||||
| 154 | def stdoutput | ||||
| 155 | STDERR.puts "stdoutput should not be used." | ||||
| 156 | @response.body | ||||
| 157 | end | ||||
| 158 | end | ||||
| 159 | end | ||||
| 160 | end | ||||
| 161 | end | ||||

