fuzed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-06-24
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2007,2008, Dave Fayram, Tom Preston-Werner
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,32 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/fuzed
7
+ bin/fuzed-adapter
8
+ bin/fuzed-conf
9
+ elibs/port_wrapper.beam
10
+ elibs/port_wrapper.erl
11
+ elibs/rails_connection_pool.beam
12
+ elibs/rails_connection_pool.erl
13
+ elibs/rails_forwarder.beam
14
+ elibs/rails_forwarder.erl
15
+ elibs/resource_manager.beam
16
+ elibs/resource_manager.erl
17
+ elibs/test_rails_handler.beam
18
+ elibs/test_rails_handler.erl
19
+ join_cluster.beam
20
+ join_cluster.erl
21
+ lib/fuzed.rb
22
+ rails_responder
23
+ sample.fuzed.conf
24
+ start_node
25
+ start_yaws
26
+ templates/fuzed.conf
27
+ testing.conf
28
+ yaws_includes/erlsom.hrl
29
+ yaws_includes/soap.hrl
30
+ yaws_includes/yaws.hrl
31
+ yaws_includes/yaws_api.hrl
32
+ yaws_includes/yaws_dav.hrl
@@ -0,0 +1,81 @@
1
+ fuzed
2
+ by Dave Fayram, Tom Preston-Werner
3
+ fuzed.rubyforge.org
4
+
5
+
6
+ == Summary
7
+ Leverage the YAWS webserver (and additional erlang-based infrastructure) to run Rails.
8
+
9
+
10
+ == Dependencies
11
+ * Erlang: http://www.erlang.org
12
+ * Yaws: http://yaws.hyber.org
13
+ * Ruby: http://www.ruby-lang.org
14
+ * Ruby Gems:
15
+ * rake: http://rake.rubyforge.org
16
+ * erlectricty: http://code.google.com/p/erlectricity
17
+ * rack: http://rack.rubyforge.org
18
+
19
+
20
+ == Installation (from gem)
21
+
22
+ gem install fuzed
23
+
24
+
25
+ == Installation (from git)
26
+
27
+ Get it from the git repo:
28
+
29
+ git clone git://repo.or.cz/fuzed.git
30
+
31
+ Change to the fuzed working copy:
32
+
33
+ cd fuzed
34
+
35
+ Build Fuzed:
36
+
37
+ rake build
38
+
39
+
40
+ == Configuration
41
+
42
+ Generate a starter Yaws config file with:
43
+
44
+ fuzed-conf RAILS_ROOT 8080
45
+
46
+ where RAILS_ROOT is the absolute path to the root directory of your Rails project. You may
47
+ optionally specify a port as the second argument. This will generate a file called
48
+ 'fuzed.conf' which contains a sample Yaws config file that should be suitable for initial
49
+ testing.
50
+
51
+
52
+ == Starting fuzed
53
+
54
+ Start the fuzed master server (yaws) locally:
55
+
56
+ fuzed start -n server@127.0.0.1 -c fuzed.conf
57
+
58
+ In another terminal, start a fuzed client locally:
59
+
60
+ fuzed join -n client@127.0.0.1 -m server@127.0.0.1 -r RAILS_ROOT
61
+
62
+ where RAILS_ROOT is the same as before.
63
+
64
+ Point your browser at:
65
+
66
+ http://localhost
67
+
68
+ If everything worked out, you'll see your Rails app!
69
+
70
+
71
+ == What is a Valid Hostname?
72
+ Erlang has a funny notion about what a valid hostname is. Localhost won't
73
+ cut it. I recommend using rendezvous to point to your local host. Short of
74
+ that, 127.0.0.1 works.
75
+
76
+
77
+ == Contribution Notes
78
+ * Please note that empty directories should contain a .placeholder file
79
+ (which should be empty), to facilitate the use of other version
80
+ control systems which bridge to subversion but don't support empty
81
+ directories.
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require './lib/fuzed.rb'
4
+
5
+ Hoe.new('fuzed', Fuzed::VERSION) do |p|
6
+ p.rubyforge_name = 'fuzed'
7
+ p.author = ['Dave Fayram', 'Tom Preston-Werner']
8
+ p.email = ['tom@rubyisawesome.com']
9
+ p.summary = 'Leverage the YAWS webserver (and additional erlang-based infrastructure) to run Rails.'
10
+ p.url = 'fuzed.rubyforge.org'
11
+ # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
12
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
13
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+ p.extra_deps << ['erlectricity', '>= 0.1.0']
15
+ end
16
+
17
+ task :build do
18
+ Dir['./**/*.erl'].each do |source|
19
+ unless File.open(source).read =~ /#!/
20
+ puts "compiling #{source}"
21
+ Dir.chdir(File.dirname(source)) do |dir|
22
+ puts `erlc #{File.basename(source)}`
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ desc 'Generate manifest from git files'
29
+ task :manifest do
30
+ sh 'git ls-tree --name-only -r HEAD > Manifest.txt'
31
+ end
32
+
33
+ desc "Upload site to Rubyforge"
34
+ task :site do
35
+ sh "scp -r site/* mojombo@god.rubyforge.org:/var/www/gforge-projects/fuzed"
36
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # require 'fuzed'
4
+ require File.join(File.dirname(__FILE__), *%w[.. lib fuzed])
5
+
6
+ require 'optparse'
7
+ require 'pp'
8
+
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: fuzed command [options]"
12
+
13
+ opts.on("-c CONFIG", "--config CONFIG", "Path to Yaws config file") do |n|
14
+ options[:config] = n
15
+ end
16
+
17
+ opts.on("-n NAME", "--name NAME", "Node name") do |n|
18
+ options[:name] = n
19
+ end
20
+
21
+ opts.on("-m NAME", "--master NAME", "Master node name") do |n|
22
+ options[:master_name] = n
23
+ end
24
+
25
+ opts.on("-r RAILS_ROOT", "--rails RAILS_ROOT", "Path to Rails root") do |a|
26
+ options[:rails] = a
27
+ end
28
+ end.parse!
29
+
30
+ command = ARGV[0]
31
+
32
+ case command
33
+ when 'start'
34
+ config = options[:config]
35
+ nodename = options[:name]
36
+
37
+ puts "Starting yaws server with name: #{nodename}"
38
+ system %Q{yaws --conf #{config} --pa #{Fuzed.relative 'elibs'} -name "#{nodename}" --runmod rails_connection_pool}
39
+ when 'join'
40
+ nodename = options[:name]
41
+ master = options[:master_name]
42
+ rails = options[:rails]
43
+
44
+ puts "Starting a client named #{nodename} offering service to #{master}"
45
+ puts "Each node will run: 'fuzed-adapter #{rails}'"
46
+ system %Q{cd #{Fuzed.root}; erl -noshell -name #{nodename} -eval "join_cluster:start('#{master}','fuzed-adapter #{rails}')."}
47
+ else
48
+ puts 'Invalid command'
49
+ end
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ rails_root = ARGV[0]
4
+
5
+ require File.join(rails_root, 'config/boot')
6
+ require RAILS_ROOT + "/config/environment"
7
+
8
+ require 'erlectricity'
9
+ require 'stringio'
10
+ require 'logger'
11
+
12
+ $logger = Logger.new(RAILS_ROOT + "/log/fuzed.#{Process.pid}.log")
13
+
14
+ def log(msg)
15
+ $logger.info(msg)
16
+ end
17
+
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
136
+
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
159
+ end
160
+
161
+ ###############################################################################
162
+
163
+ unless defined? RAILS_ROOT
164
+ raise "Rails' environment has to be loaded before using Rack::Adapter::Rails"
165
+ end
166
+
167
+ require "rack/request"
168
+ require "rack/response"
169
+ require "dispatcher"
170
+
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
272
+ end
273
+
274
+ ###############################################################################
275
+
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))
281
+
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]]
289
+
290
+ p Rack::Handler::Fuzed.new(Rack::Adapter::Rails.new).service(req)
291
+ end