moved rack.rb outside of rails app
[fuzed.git] / lib / fuzed / rack.rb
blobd2bf506c0a9ff1738a6caf7892ddac8bca90ef5e
1 #!/usr/bin/env ruby
3 rails_root = ARGV[0]
5 require File.join(rails_root, 'config/boot')
6 require RAILS_ROOT + "/config/environment"
8 require 'erlectricity'
9 require 'stringio'
10 require 'logger'
12 $logger = Logger.new(RAILS_ROOT + "/log/fuzed.#{Process.pid}.log")
14 def log(msg)
15   $logger.info(msg)
16 end
18 module Rack
19   module Handler
20     class Fuzed
21       def self.run(app)
22         Fuzed.new(app).listen
23       end
24       
25       def initialize(app)
26         @app = app
27       end
28       
29       def listen
30         log "Waiting for connections"
31         
32         me = self
33         
34         receive(IO.new(3), IO.new(4)) do
35           match(:request, list(:request)) do
36             $t = Time.now
37             log('------------MATCH------------')
38             log request.inspect
39             res = me.service(request)
40             send!(res)
41             log(">> Total in " + (Time.now - $t).to_s + " sec\n")
42             receive_loop
43           end
44           
45           match(:ping) do
46             send!(:pong)
47             receive_loop
48           end
49           
50           match(any(:any)) do
51             log('------------NO-MATCH------------')
52             log any.inspect
53             receive_loop
54           end
55         end
56       end
57       
58       def service(vars)
59         request = vars.inject({}) { |a, x| a[x[0]] = x[1]; a }
60         
61         method = request[:method]
62         version = request[:http_version] # => e.g. [1, 1]
63         path = request[:querypath]
64         query = request[:querydata]
65         server = request[:servername]
66         headers = request[:headers]
67         cookies = request[:cookies]
68         postdata = request[:postdata] == :undefined ? '' : request[:postdata]
69                 
70         translate = {:content_type => 'CONTENT_TYPE',
71                      :content_length => 'CONTENT_LENGTH',
72                      :accept => 'HTTP_ACCEPT',
73                      :'Accept-Charset' => 'HTTP_ACCEPT_CHARSET',
74                      :'Accept-Encoding' => 'HTTP_ACCEPT_ENCODING',
75                      :'Accept-Language' => 'HTTP_ACCEPT_LANGUAGE',
76                      :connection => 'HTTP_CONNECTION',
77                      :keep_alive => 'HTTP_KEEP_ALIVE',
78                      :host => 'HTTP_HOST',
79                      :referer => 'HTTP_REFERER',
80                      :user_agent => 'HTTP_USER_AGENT',
81                      'X-Prototype-Version' => 'HTTP_X_PROTOTYPE_VERSION',
82                      'X-Requested-With' => 'HTTP_X_REQUESTED_WITH'}
83                      
84         env = {}
85         env['REQUEST_METHOD'] = method.to_s
86         env['QUERY_STRING'] = query
87         env["PATH_INFO"] = path == '/' ? '' : path
88         env = headers.inject(env) { |a, x| a[translate[x[0]] || x[0].to_s] = x[1]; a }
89         env.delete_if { |k, v| v.nil? }
90         
91         env.update({"rack.version" => [0,2],
92                      "rack.input" => StringIO.new(postdata),
93                      "rack.errors" => STDERR,
94                      
95                      "rack.multithread" => true,
96                      "rack.multiprocess" => false,
97                      "rack.run_once" => false,
98                      
99                      "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
100                    })
101                    
102         env['SERVER_NAME'] = server.split(':')[0]
103         env['SERVER_PORT'] = server.split(':')[1]
104         env['HTTP_VERSION'] = version.join('.')
105         
106         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
107         env["QUERY_STRING"] ||= ""
108         env["REQUEST_PATH"] ||= "/"
109         env.delete "PATH_INFO" if env["PATH_INFO"] == ""
110         
111         cookies.each do |cookie|
112           env["HTTP_COOKIE"] = cookie.to_s
113         end
114         
115         log('------------IN------------')
116         log(env.inspect)
117         
118         begin
119           t1 = Time.now
120           status, headers, body = @app.call(env)
121           log(">> Rails in " + (Time.now - t1).to_s + " sec")
122         
123           html = ''
124           body.each do |part|
125             html << part
126           end
127           
128           headers['Server'] = 'YAWS + Fuzed 0.0.1'
129           headers['Connection'] = 'close'
130           
131           cookies = headers.delete('cookie')
132           #cookies.map! {|c| c.include?('path=') ? c : c + "; path=/"}
133           headers['Set-Cookie'] = cookies if cookies
134           
135           # p headers
137           res = 
138           [:response,
139            [[:status, status.to_i],
140             [:allheaders, headers.inject([]) { |a, x| a += x[1].map { |y| [:header, x[0], y] } }],
141             [:html, html]]]
142         rescue => e
143           res = 
144           [:response, 
145             [[:status, 500],
146              [:allheaders, [
147                [:header, "Content-Type", "text/plain; charset=utf-8"], 
148                [:header, "Cache-Control", "no-cache"]]], 
149              [:html, "500 Internal Error\n\n#{e}\n\n#{e.backtrace}"]]]
150         end
151           
152         log('-----------OUT------------')
153         log(res.inspect)
154         
155         res
156       end
157     end
158   end
161 ###############################################################################
163 unless defined? RAILS_ROOT
164   raise "Rails' environment has to be loaded before using Rack::Adapter::Rails"
167 require "rack/request"
168 require "rack/response"
169 require "dispatcher"
171 module Rack
172   module Adapter 
173     class Rails
174       def call(env)
175         request = Request.new(env)
176         response = Response.new
177         
178         cgi = CGIStub.new(request, response)
179         
180         Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response)
181         
182         response.finish
183       end
184       
185       protected
186       
187       class CGIStub < ::CGI
188         
189         def initialize(request, response, *args)
190           @request = request
191           @response = response
192           @args = *args
193           @input = request.body
194           super(*args)
195         end
196         
197         IGNORED_HEADERS = [ "Status" ]
198         
199         def header(options = "text/html")
200           # puts 'header---------------'
201           # p options
202           # puts '---------------------'
203           
204           if options.instance_of?(String)
205             @response['Content-Type'] = options unless @response['Content-Type']
206           else
207             @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
208             
209             @response['Content-Type'] = options.delete('type') || "text/html"
210             @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
211                         
212             @response['Status'] = options.delete('status') if options['status']
213             @response['Content-Language'] = options.delete('language') if options['language']
214             @response['Expires'] = options.delete('expires') if options['expires']
215         
216             IGNORED_HEADERS.each {|k| options.delete(k) }
217         
218             options.each{|k,v| @response[k] = v}
219             
220             # convert 'cookie' header to 'Set-Cookie' headers
221             if cookie = @response['cookie']
222               case cookie
223                 when Array
224                   cookie.each {|c| @response['Set-Cookie'] = c.to_s }
225                 when Hash
226                   cookie.each_value {|c| @response['Set-Cookie'] = c.to_s}
227                 else
228                   @response['Set-Cookie'] = options['cookie'].to_s
229               end
230         
231               @output_cookies.each { |c| @response['Set-Cookie'] = c.to_s } if @output_cookies
232             end
233           end
234         
235           ""
236         end
237                         
238         def params
239           @request.params
240         end
241         
242         def cookies
243           @request.cookies
244         end
245         
246         def query_string
247           @request.query_string
248         end
249           
250         # Used to wrap the normal args variable used inside CGI.
251         def args
252           @args
253         end
254     
255         # Used to wrap the normal env_table variable used inside CGI.
256         def env_table
257           @request.env
258         end
259     
260         # Used to wrap the normal stdinput variable used inside CGI.
261         def stdinput
262           @input
263         end
264         
265         def stdoutput
266           STDERR.puts "stdoutput should not be used."
267           @response.body
268         end  
269       end
270     end
271   end
274 ###############################################################################
276 require 'rack'
277 require 'rack/cascade'
278 require 'rack/showexceptions'
279 # Rack::Handler::Fuzed.run \
280 #   Rack::ShowExceptions.new(Rack::Lint.new(Rack::Adapter::Rails.new))
282 if ARGV.first != 'test'
283   Rack::Handler::Fuzed.run(Rack::Adapter::Rails.new)
284 else
285   req = 
286   [[:method, :POST], [:http_version, [1, 1]], [:querypath, "/main/go"], [:querydata, ""], [:servername, "testing:8002"], [:headers, [[:connection, "keep-alive"], [:accept, "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"], [:host, "localhost:8002"], [:referer, "http://localhost:8002/main/ready"], [:user_agent, "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"], [:keep_alive, "300"], [:content_length, "7"], [:content_type, "application/x-www-form-urlencoded"], [:"Cache-Control", "max-age=0"], [:"Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"], [:"Accept-Encoding", "gzip,deflate"], [:"Accept-Language", "en-us,en;q=0.5"]]], [:cookies, ["_helloworld_session_id=d3eae987aab3230377abc433b7a8d7c1"]], [:pathinfo, "/Users/tom/dev/fuzed/helloworld/public"], [:postdata, "val=foo"]]
287   
288   # [[:method, :GET], [:http_version, [1, 1]], [:querypath, "/main/say"], [:querydata, ""], [:servername, "testing:8002"], [:headers, [[:connection, "keep-alive"], [:accept, "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"], [:host, "localhost:8002"], [:user_agent, "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"], [:keep_alive, "300"], [:"Cache-Control", "max-age=0"], [:"Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"], [:"Accept-Encoding", "gzip,deflate"], [:"Accept-Language", "en-us,en;q=0.5"]]], [:cookies, ["_helloworld_session_id=166098a3c3f702698d0529c6148c6164"]], [:pathinfo, "/Users/tom/dev/fuzed/helloworld/public"], [:postdata, :undefined]]
290   p Rack::Handler::Fuzed.new(Rack::Adapter::Rails.new).service(req)