fuzed 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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