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