├── .gitignore ├── COPYING ├── History.txt ├── LICENSE ├── Manifest.txt ├── README.rdoc ├── Rakefile ├── TODO ├── bin └── mongrel_rails ├── examples ├── builder.rb ├── camping │ ├── README │ ├── blog.rb │ └── tepee.rb ├── httpd.conf ├── mime.yaml ├── mongrel.conf ├── monitrc ├── random_thrash.rb ├── simpletest.rb └── webrick_compare.rb ├── ext └── http11 │ ├── Http11Service.java │ ├── ext_help.h │ ├── extconf.rb │ ├── http11.c │ ├── http11_parser.c │ ├── http11_parser.h │ ├── http11_parser.java.rl │ ├── http11_parser.rl │ ├── http11_parser_common.rl │ └── org │ └── jruby │ └── mongrel │ ├── Http11.java │ └── Http11Parser.java ├── lib ├── mongrel.rb └── mongrel │ ├── camping.rb │ ├── cgi.rb │ ├── command.rb │ ├── configurator.rb │ ├── const.rb │ ├── debug.rb │ ├── gems.rb │ ├── handlers.rb │ ├── header_out.rb │ ├── http_request.rb │ ├── http_response.rb │ ├── init.rb │ ├── mime_types.yml │ ├── rails.rb │ ├── stats.rb │ ├── tcphack.rb │ └── uri_classifier.rb ├── mongrel-public_cert.pem ├── projects ├── cgi_multipart_eof_fix │ ├── CHANGELOG │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── lib │ │ └── cgi_multipart_eof_fix.rb │ └── test │ │ └── test_cgi_multipart_eof_fix.rb ├── fastthread │ ├── CHANGELOG │ ├── Manifest │ ├── Rakefile │ ├── ext │ │ └── fastthread │ │ │ ├── .gitignore │ │ │ ├── extconf.rb │ │ │ └── fastthread.c │ ├── setup.rb │ └── test │ │ ├── test_all.rb │ │ ├── test_condvar.rb │ │ ├── test_mutex.rb │ │ └── test_queue.rb ├── gem_plugin │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── bin │ │ └── gpgen │ ├── lib │ │ └── gem_plugin.rb │ ├── resources │ │ ├── COPYING │ │ ├── LICENSE │ │ ├── README │ │ ├── Rakefile │ │ ├── lib │ │ │ └── project │ │ │ │ └── init.rb │ │ └── resources │ │ │ └── defaults.yaml │ ├── setup.rb │ └── test │ │ └── test_plugins.rb ├── mongrel_cluster │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── bin │ │ └── mongrel_cluster_ctl │ ├── lib │ │ └── mongrel_cluster │ │ │ ├── init.rb │ │ │ ├── recipes.rb │ │ │ ├── recipes_1.rb │ │ │ └── recipes_2.rb │ └── resources │ │ ├── defaults.yaml │ │ └── mongrel_cluster ├── mongrel_config │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── lib │ │ └── mongrel_config │ │ │ ├── app.rb │ │ │ ├── init.rb │ │ │ ├── win32.rb │ │ │ └── win32_app.rb │ ├── resources │ │ ├── defaults.yaml │ │ ├── images │ │ │ ├── 0170_bubble.png │ │ │ ├── 0171_new_page.png │ │ │ ├── bottom.gif │ │ │ ├── bottom_orig.gif │ │ │ ├── middle.jpg │ │ │ ├── top.jpg │ │ │ └── topbar.jpg │ │ ├── index.html │ │ ├── index_win32.html │ │ └── style.css │ └── test │ │ └── test_config.rb ├── mongrel_console │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── lib │ │ └── mongrel_console │ │ │ ├── console.rb │ │ │ └── init.rb │ └── resources │ │ └── defaults.yaml ├── mongrel_experimental │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── ext │ │ └── uri_classifier │ │ │ ├── ext_help.h │ │ │ ├── extconf.rb │ │ │ ├── tst.h │ │ │ ├── tst_cleanup.c │ │ │ ├── tst_delete.c │ │ │ ├── tst_grow_node_free_list.c │ │ │ ├── tst_init.c │ │ │ ├── tst_insert.c │ │ │ ├── tst_search.c │ │ │ └── uri_classifier.c │ ├── lib │ │ └── mongrel_experimental.rb │ └── test │ │ └── test_uriclassifier.rb ├── mongrel_service │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── TODO │ ├── lib │ │ ├── ServiceFB │ │ │ ├── ServiceFB.bas │ │ │ ├── ServiceFB.bi │ │ │ ├── ServiceFB_Utils.bas │ │ │ ├── ServiceFB_Utils.bi │ │ │ ├── _internals.bi │ │ │ └── _utils_internals.bi │ │ └── mongrel_service │ │ │ └── init.rb │ ├── native │ │ ├── _debug.bi │ │ ├── boolean.bi │ │ ├── console_process.bas │ │ ├── console_process.bi │ │ ├── mongrel_service.bas │ │ └── mongrel_service.bi │ ├── resources │ │ └── defaults.yaml │ └── tools │ │ └── freebasic.rb ├── mongrel_status │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ └── lib │ │ └── mongrel_status │ │ └── init.rb └── mongrel_upload_progress │ ├── CHANGELOG │ ├── COPYING │ ├── LICENSE │ ├── Manifest │ ├── README │ ├── Rakefile │ ├── lib │ └── mongrel_upload_progress │ │ └── init.rb │ └── resources │ └── defaults.yaml ├── setup.rb ├── tasks ├── gem.rake ├── java.rake ├── native.rake └── ragel.rake ├── test ├── mime.yaml ├── mongrel.conf ├── test_cgi_wrapper.rb ├── test_command.rb ├── test_conditional.rb ├── test_configurator.rb ├── test_debug.rb ├── test_handlers.rb ├── test_http11.rb ├── test_redirect_handler.rb ├── test_request_progress.rb ├── test_response.rb ├── test_stats.rb ├── test_uriclassifier.rb ├── test_ws.rb ├── testhelp.rb └── unit │ └── test_http_parser.rb.orig └── tools └── trickletest.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.bundle 2 | *.log 3 | *.o 4 | *.so 5 | *.jar 6 | *.rbc 7 | doc 8 | log 9 | pkg 10 | tmp 11 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | You can redistribute it and/or modify it under 3 | either the terms of the GPL or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 1.2.0.beta.1 / 2010-07-?? 2 | 3 | Improvements: 4 | 5 | * Ruby 1.9 early compatbility: Merged commits form Eric Wong and Matt Aimonetti. 6 | * Better RubyGems support thanks to added env she-bang to mongrel_rails executable. 7 | * Smartly load http11 extension using fat-binary approach. 8 | * Better detection of Windows platform (usage of RbConfig::CONFIG instead of RUBY_PLATFORM) 9 | 10 | Bugfixes: 11 | 12 | * Fixed proper version reporting under 1.9 13 | * Consider MinGW as valid Windows platform. 14 | * Properly drop PID when daemonizing (EngineYard patched 1.1.5.1). 15 | 16 | Reorganization: 17 | 18 | * Rake task project reorganization and reformat using Hoe. 19 | * Compile extension using rake-compiler 20 | * Upgraded http11_parser to work with Ragel 6.2 21 | * Deprecated obsolete Windows service example scripts (from 2006!) 22 | * Relocate Http11 JRuby extension to share http11 C extension folder and name. 23 | 24 | === 1.1.5 / 2008-05-22 25 | 26 | * Fix bug where num_processors is not actually set from mongrel_rails. 27 | 28 | 29 | === 1.1.4 / 2008-02-29 30 | 31 | * Fix camping handler. Correct treatment of @throttle parameter. 32 | 33 | 34 | === 1.1.3 / 2007-12-29 35 | 36 | * Fix security flaw of DirHandler; reported on mailing list. 37 | 38 | 39 | === 1.1.2 / 2007-12-15 40 | 41 | * Fix worker termination bug; fix JRuby 1.0.3 load order issue; fix require issue on systems without Rubygems. 42 | 43 | 44 | === 1.1.1 / 2007-11-12 45 | 46 | * Fix mongrel_rails restart bug; fix bug with Rack status codes. 47 | 48 | 49 | === 1.1 / 2007-11-01 50 | 51 | * Pure Ruby URIClassifier. 52 | * More modular architecture. 53 | * JRuby support. 54 | * Move C URIClassifier into mongrel_experimental project. 55 | 56 | 57 | === 1.0.4 / 2007-10-29 58 | 59 | * Backport fixes for versioning inconsistency, mongrel_rails bug, and DirHandler bug. 60 | 61 | 62 | === 1.0.3 63 | 64 | * Fix user-switching bug; make people upgrade to the latest from the RC. 65 | 66 | 67 | === 1.0.2 68 | 69 | * Signed gem; many minor bugfixes and patches. 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | COPYING 2 | History.txt 3 | LICENSE 4 | Manifest.txt 5 | README.txt 6 | Rakefile 7 | TODO 8 | bin/mongrel_rails 9 | examples/builder.rb 10 | examples/camping/README 11 | examples/camping/blog.rb 12 | examples/camping/tepee.rb 13 | examples/httpd.conf 14 | examples/mime.yaml 15 | examples/mongrel.conf 16 | examples/monitrc 17 | examples/random_thrash.rb 18 | examples/simpletest.rb 19 | examples/webrick_compare.rb 20 | ext/http11/ext_help.h 21 | ext/http11/extconf.rb 22 | ext/http11/http11.c 23 | ext/http11/http11_parser.c 24 | ext/http11/http11_parser.h 25 | ext/http11/http11_parser.java.rl 26 | ext/http11/http11_parser.rl 27 | ext/http11/http11_parser_common.rl 28 | ext/http11/Http11Service.java 29 | ext/http11/org/jruby/mongrel/Http11.java 30 | ext/http11/org/jruby/mongrel/Http11Parser.java 31 | lib/mongrel.rb 32 | lib/mongrel/camping.rb 33 | lib/mongrel/cgi.rb 34 | lib/mongrel/command.rb 35 | lib/mongrel/configurator.rb 36 | lib/mongrel/const.rb 37 | lib/mongrel/debug.rb 38 | lib/mongrel/gems.rb 39 | lib/mongrel/handlers.rb 40 | lib/mongrel/header_out.rb 41 | lib/mongrel/http_request.rb 42 | lib/mongrel/http_response.rb 43 | lib/mongrel/init.rb 44 | lib/mongrel/mime_types.yml 45 | lib/mongrel/rails.rb 46 | lib/mongrel/stats.rb 47 | lib/mongrel/tcphack.rb 48 | lib/mongrel/uri_classifier.rb 49 | setup.rb 50 | tasks/gem.rake 51 | tasks/native.rake 52 | tasks/ragel.rake 53 | test/mime.yaml 54 | test/mongrel.conf 55 | test/test_cgi_wrapper.rb 56 | test/test_command.rb 57 | test/test_conditional.rb 58 | test/test_configurator.rb 59 | test/test_debug.rb 60 | test/test_handlers.rb 61 | test/test_http11.rb 62 | test/test_redirect_handler.rb 63 | test/test_request_progress.rb 64 | test/test_response.rb 65 | test/test_stats.rb 66 | test/test_uriclassifier.rb 67 | test/test_ws.rb 68 | test/testhelp.rb 69 | tools/trickletest.rb 70 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Mongrel: Simple Fast Mostly Ruby Web Server 2 | 3 | * http://mongrel.rubyforge.org/ 4 | * http://rubyforge.org/projects/mongrel 5 | * http://github.com/luislavena/mongrel 6 | 7 | == DEPRECATED 8 | 9 | Please use Unicorn[http://unicorn.bogomips.org/] instead. 10 | 11 | == Description 12 | 13 | Mongrel is a small library that provides a very fast HTTP 1.1 server for Ruby web applications. It is not particular to any framework, and is intended to be just enough to get a web application running behind a more complete and robust web server. 14 | 15 | What makes Mongrel so fast is the careful use of an Ragel extension to provide fast, accurate HTTP 1.1 protocol parsing. This makes the server scream without too many portability issues. 16 | 17 | See http://mongrel.rubyforge.org for more information. 18 | 19 | == License 20 | 21 | Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed under the Ruby license and the GPL2. See the include LICENSE file for details. 22 | 23 | == Quick Start 24 | 25 | The easiest way to get started with Mongrel is to install it via RubyGems and then run a Ruby on Rails application. You can do this easily: 26 | 27 | $ gem install mongrel 28 | 29 | Now you should have the mongrel_rails command available in your PATH, so just do the following: 30 | 31 | $ cd myrailsapp 32 | $ mongrel_rails start 33 | 34 | This will start it in the foreground so you can play with it. It runs your application in production mode. To get help do: 35 | 36 | $ mongrel_rails start -h 37 | 38 | Finally, you can then start in background mode: 39 | 40 | $ mongrel_rails start -d 41 | 42 | And you can stop it whenever you like with: 43 | 44 | $ mongrel_rails stop 45 | 46 | All of which should be done from your application's directory. It writes the PID of the process you ran into log/mongrel.pid. 47 | 48 | There are also many more new options for configuring the rails runner including changing to a different directory, adding more MIME types, and setting processor threads and timeouts. 49 | 50 | == Install 51 | 52 | It doesn't explicitly require Camping, but if you want to run the examples/camping/ examples then you'll need to install Camping 1.2 at least (and redcloth I think). These are all available from RubyGems. 53 | 54 | The library consists of a C extension so you'll need a C compiler or at least a friend who can build it for you. 55 | 56 | Finally, the source includes a setup.rb for those who hate RubyGems. 57 | 58 | == Usage 59 | 60 | The examples/simpletest.rb file has the following code as the simplest example: 61 | 62 | require 'mongrel' 63 | 64 | class SimpleHandler < Mongrel::HttpHandler 65 | def process(request, response) 66 | response.start(200) do |head,out| 67 | head["Content-Type"] = "text/plain" 68 | out.write("hello!\n") 69 | end 70 | end 71 | end 72 | 73 | h = Mongrel::HttpServer.new("0.0.0.0", "3000") 74 | h.register("/test", SimpleHandler.new) 75 | h.register("/files", Mongrel::DirHandler.new(".")) 76 | h.run.join 77 | 78 | If you run this and access port 3000 with a browser it will say "hello!". If you access it with any url other than "/test" it will give a simple 404. Check out the Mongrel::Error404Handler for a basic way to give a more complex 404 message. 79 | 80 | This also shows the DirHandler with directory listings. This is still rough but it should work for basic hosting. *File extension to mime type mapping is missing though.* 81 | 82 | == Contact 83 | 84 | E-mail the Mongrel list at http://rubyforge.org/mailman/listinfo/mongrel-users and someone will help you. Comments about the API are welcome. 85 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: Keep this file clean. 3 | # Add your customizations inside tasks directory. 4 | # Thank You. 5 | # 6 | 7 | # load rakefile extensions (tasks) 8 | Dir['tasks/*.rake'].sort.each { |f| load f } 9 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | v1.2. Rewrite and merge mongrel cluster and mongrel_rails into something small and maintainable. Remove gem_plugin entirely. 3 | 4 | v1.1.1. See if Java is setting the server version string in the request properly. 5 | 6 | -------------------------------------------------------------------------------- /examples/builder.rb: -------------------------------------------------------------------------------- 1 | require 'mongrel' 2 | 3 | class TestPlugin < GemPlugin::Plugin "/handlers" 4 | include Mongrel::HttpHandlerPlugin 5 | 6 | def process(request, response) 7 | STDERR.puts "My options are: #{options.inspect}" 8 | STDERR.puts "Request Was:" 9 | STDERR.puts request.params.to_yaml 10 | end 11 | end 12 | 13 | config = Mongrel::Configurator.new :host => "127.0.0.1" do 14 | load_plugins :includes => ["mongrel"], :excludes => ["rails"] 15 | daemonize :cwd => Dir.pwd, :log_file => "mongrel.log", :pid_file => "mongrel.pid" 16 | 17 | listener :port => 3000 do 18 | uri "/app", :handler => plugin("/handlers/testplugin", :test => "that") 19 | uri "/app", :handler => Mongrel::DirHandler.new(".") 20 | load_plugins :includes => ["mongrel", "rails"] 21 | end 22 | 23 | trap("INT") { stop } 24 | run 25 | end 26 | 27 | config.join 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/camping/README: -------------------------------------------------------------------------------- 1 | To get these examples running, install Camping. 2 | 3 | Instructions here: http://code.whytheluckystiff.net/camping/ 4 | -------------------------------------------------------------------------------- /examples/mime.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | .jpeg: image/jpeg 3 | .png: image/test 4 | -------------------------------------------------------------------------------- /examples/mongrel.conf: -------------------------------------------------------------------------------- 1 | --- 2 | :environment: production 3 | :daemon: "true" 4 | :host: 0.0.0.0 5 | :log_file: log/mongrel.log 6 | :docroot: public 7 | :debug: "false" 8 | :port: 3000 9 | :pid_file: log/mongrel.pid 10 | -------------------------------------------------------------------------------- /examples/monitrc: -------------------------------------------------------------------------------- 1 | set daemon 60 2 | set logfile syslog facility log_daemon 3 | set mailserver localhost 4 | set mail-format { from: monit@localhost } 5 | set alert root@localhost 6 | 7 | check process sshd with pidfile /var/run/sshd.pid 8 | start program "/etc/init.d/ssh start" 9 | stop program "/etc/init.d/ssh stop" 10 | if failed port 22 protocol ssh then restart 11 | if 5 restarts within 5 cycles then timeout 12 | 13 | check process mysql with pidfile /var/run/mysqld/mysqld.pid 14 | group database 15 | start program = "/etc/init.d/mysql start" 16 | stop program = "/etc/init.d/mysql stop" 17 | if failed host 127.0.0.1 port 3306 then restart 18 | if 5 restarts within 5 cycles then timeout 19 | 20 | check process httpd with pidfile /usr/local/apache2/logs/httpd.pid 21 | group www-data 22 | start program "/usr/local/apache2/bin/apachectl start" 23 | stop program "/usr/local/apache2/bin/apachectl stop" 24 | if failed host localhost port 80 protocol http 25 | and request "/" then alert 26 | if cpu is greater than 60% for 2 cycles then alert 27 | if cpu > 80% for 5 cycles then restart 28 | if children > 250 then restart 29 | if loadavg(5min) greater than 10 for 8 cycles then alert 30 | if 3 restarts within 5 cycles then timeout 31 | 32 | check process mongrel_8000 with pidfile /var/rails/MYAPP/log/mongrel.8000.pid 33 | group root 34 | if failed host 127.0.0.1 port 8000 protocol http 35 | and request "/" then alert 36 | if cpu is greater than 60% for 2 cycles then alert 37 | if cpu > 80% for 5 cycles then restart 38 | if loadavg(5min) greater than 10 for 8 cycles then restart 39 | if 3 restarts within 5 cycles then timeout 40 | 41 | check process mongrel_8001 with pidfile /var/rails/MYAPP/log/mongrel.8001.pid 42 | group root 43 | if failed host 127.0.0.1 port 8001 protocol http 44 | and request "/" then alert 45 | if cpu is greater than 60% for 2 cycles then alert 46 | if cpu > 80% for 5 cycles then alert 47 | if loadavg(5min) greater than 10 for 8 cycles then alert 48 | if 3 restarts within 5 cycles then timeout 49 | 50 | check process postfix with pidfile /var/spool/postfix/pid/master.pid 51 | group mail 52 | start program = "/etc/init.d/postfix start" 53 | stop program = "/etc/init.d/postfix stop" 54 | if failed port 25 protocol smtp then restart 55 | if 5 restarts within 5 cycles then timeout 56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/random_thrash.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | devrand = open("/dev/random","r") 3 | 4 | loop do 5 | s = TCPSocket.new(ARGV[0],ARGV[1]) 6 | s.write("GET / HTTP/1.1\r\n") 7 | total = 0 8 | begin 9 | loop do 10 | r = devrand.read(10) 11 | n = s.write(r) 12 | total += n 13 | end 14 | rescue Object 15 | STDERR.puts "#$!: #{total}" 16 | end 17 | s.close 18 | sleep 1 19 | end 20 | -------------------------------------------------------------------------------- /examples/simpletest.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib") 2 | require 'mongrel' 3 | require 'yaml' 4 | 5 | class SimpleHandler < Mongrel::HttpHandler 6 | def process(request, response) 7 | response.start do |head,out| 8 | head["Content-Type"] = "text/html" 9 | results = "Your request:
#{request.params.to_yaml}
View the files." 10 | out << results 11 | end 12 | end 13 | end 14 | 15 | class DumbHandler < Mongrel::HttpHandler 16 | def process(request, response) 17 | response.start do |head,out| 18 | head["Content-Type"] = "text/html" 19 | out.write("test") 20 | end 21 | end 22 | end 23 | 24 | 25 | if ARGV.length != 3 26 | STDERR.puts "usage: simpletest.rb " 27 | exit(1) 28 | end 29 | 30 | stats = Mongrel::StatisticsFilter.new(:sample_rate => 1) 31 | 32 | config = Mongrel::Configurator.new :host => ARGV[0], :port => ARGV[1] do 33 | listener do 34 | uri "/", :handler => SimpleHandler.new 35 | uri "/", :handler => Mongrel::DeflateFilter.new 36 | uri "/", :handler => stats 37 | uri "/dumb", :handler => DumbHandler.new 38 | uri "/dumb", :handler => Mongrel::DeflateFilter.new 39 | uri "/dumb", :handler => stats 40 | uri "/files", :handler => Mongrel::DirHandler.new(ARGV[2]) 41 | uri "/files", :handler => stats 42 | uri "/status", :handler => Mongrel::StatusHandler.new(:stats_filter => stats) 43 | redirect "/redir1", "/" 44 | redirect "/to", /to/, 'w' 45 | end 46 | 47 | trap("INT") { stop } 48 | run 49 | end 50 | 51 | puts "Mongrel running on #{ARGV[0]}:#{ARGV[1]} with docroot #{ARGV[2]}" 52 | config.join 53 | -------------------------------------------------------------------------------- /examples/webrick_compare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/ruby 2 | require 'webrick' 3 | include WEBrick 4 | 5 | s = HTTPServer.new( :Port => 4000 ) 6 | 7 | # HTTPServer#mount(path, servletclass) 8 | # When a request referring "/hello" is received, 9 | # the HTTPServer get an instance of servletclass 10 | # and then call a method named do_"a HTTP method". 11 | 12 | class HelloServlet < HTTPServlet::AbstractServlet 13 | def do_GET(req, res) 14 | res.body = "hello!" 15 | res['Content-Type'] = "text/html" 16 | end 17 | end 18 | s.mount("/test", HelloServlet) 19 | 20 | s.start -------------------------------------------------------------------------------- /ext/http11/Http11Service.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | 3 | import org.jruby.Ruby; 4 | import org.jruby.runtime.load.BasicLibraryService; 5 | 6 | import org.jruby.mongrel.Http11; 7 | 8 | public class Http11Service implements BasicLibraryService { 9 | public boolean basicLoad(final Ruby runtime) throws IOException { 10 | Http11.createHttp11(runtime); 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ext/http11/ext_help.h: -------------------------------------------------------------------------------- 1 | #ifndef ext_help_h 2 | #define ext_help_h 3 | 4 | #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be."); 5 | #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name); 6 | #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T); 7 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 8 | 9 | #ifdef DEBUG 10 | #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__) 11 | #else 12 | #define TRACE() 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /ext/http11/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | dir_config("http11") 4 | have_library("c", "main") 5 | 6 | create_makefile("http11") 7 | -------------------------------------------------------------------------------- /ext/http11/http11_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2005 Zed A. Shaw 3 | * You can redistribute it and/or modify it under the same terms as Ruby. 4 | */ 5 | 6 | #ifndef http11_parser_h 7 | #define http11_parser_h 8 | 9 | #include 10 | 11 | #if defined(_WIN32) 12 | #include 13 | #endif 14 | 15 | typedef void (*element_cb)(void *data, const char *at, size_t length); 16 | typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen); 17 | 18 | typedef struct http_parser { 19 | int cs; 20 | size_t body_start; 21 | int content_len; 22 | size_t nread; 23 | size_t mark; 24 | size_t field_start; 25 | size_t field_len; 26 | size_t query_start; 27 | 28 | void *data; 29 | 30 | field_cb http_field; 31 | element_cb request_method; 32 | element_cb request_uri; 33 | element_cb fragment; 34 | element_cb request_path; 35 | element_cb query_string; 36 | element_cb http_version; 37 | element_cb header_done; 38 | 39 | } http_parser; 40 | 41 | int http_parser_init(http_parser *parser); 42 | int http_parser_finish(http_parser *parser); 43 | size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off); 44 | int http_parser_has_error(http_parser *parser); 45 | int http_parser_is_finished(http_parser *parser); 46 | 47 | #define http_parser_nread(parser) (parser)->nread 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /ext/http11/http11_parser.java.rl: -------------------------------------------------------------------------------- 1 | package org.jruby.mongrel; 2 | 3 | import org.jruby.util.ByteList; 4 | 5 | public class Http11Parser { 6 | 7 | /** Machine **/ 8 | 9 | %%{ 10 | 11 | machine http_parser; 12 | 13 | action mark {parser.mark = fpc; } 14 | 15 | action start_field { parser.field_start = fpc; } 16 | action snake_upcase_field { /* FIXME stub */ } 17 | action write_field { 18 | parser.field_len = fpc-parser.field_start; 19 | } 20 | 21 | action start_value { parser.mark = fpc; } 22 | action write_value { 23 | if(parser.http_field != null) { 24 | parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark); 25 | } 26 | } 27 | action request_method { 28 | if(parser.request_method != null) 29 | parser.request_method.call(parser.data, parser.mark, fpc-parser.mark); 30 | } 31 | action request_uri { 32 | if(parser.request_uri != null) 33 | parser.request_uri.call(parser.data, parser.mark, fpc-parser.mark); 34 | } 35 | action fragment { 36 | if(parser.fragment != null) 37 | parser.fragment.call(parser.data, parser.mark, fpc-parser.mark); 38 | } 39 | 40 | action start_query {parser.query_start = fpc; } 41 | action query_string { 42 | if(parser.query_string != null) 43 | parser.query_string.call(parser.data, parser.query_start, fpc-parser.query_start); 44 | } 45 | 46 | action http_version { 47 | if(parser.http_version != null) 48 | parser.http_version.call(parser.data, parser.mark, fpc-parser.mark); 49 | } 50 | 51 | action request_path { 52 | if(parser.request_path != null) 53 | parser.request_path.call(parser.data, parser.mark, fpc-parser.mark); 54 | } 55 | 56 | action done { 57 | parser.body_start = fpc + 1; 58 | if(parser.header_done != null) 59 | parser.header_done.call(parser.data, fpc + 1, pe - fpc - 1); 60 | fbreak; 61 | } 62 | 63 | include http_parser_common "http11_parser_common.rl"; 64 | 65 | }%% 66 | 67 | /** Data **/ 68 | %% write data; 69 | 70 | public static interface ElementCB { 71 | public void call(Object data, int at, int length); 72 | } 73 | 74 | public static interface FieldCB { 75 | public void call(Object data, int field, int flen, int value, int vlen); 76 | } 77 | 78 | public static class HttpParser { 79 | int cs; 80 | int body_start; 81 | int content_len; 82 | int nread; 83 | int mark; 84 | int field_start; 85 | int field_len; 86 | int query_start; 87 | 88 | Object data; 89 | ByteList buffer; 90 | 91 | public FieldCB http_field; 92 | public ElementCB request_method; 93 | public ElementCB request_uri; 94 | public ElementCB fragment; 95 | public ElementCB request_path; 96 | public ElementCB query_string; 97 | public ElementCB http_version; 98 | public ElementCB header_done; 99 | 100 | public void init() { 101 | cs = 0; 102 | 103 | %% write init; 104 | 105 | body_start = 0; 106 | content_len = 0; 107 | mark = 0; 108 | nread = 0; 109 | field_len = 0; 110 | field_start = 0; 111 | } 112 | } 113 | 114 | public final HttpParser parser = new HttpParser(); 115 | 116 | public int execute(ByteList buffer, int off) { 117 | int p, pe; 118 | int cs = parser.cs; 119 | int len = buffer.length(); 120 | assert off<=len : "offset past end of buffer"; 121 | 122 | p = off; 123 | pe = len; 124 | byte[] data = buffer.unsafeBytes(); 125 | parser.buffer = buffer; 126 | 127 | %% write exec; 128 | 129 | parser.cs = cs; 130 | parser.nread += (p - off); 131 | 132 | assert p <= pe : "buffer overflow after parsing execute"; 133 | assert parser.nread <= len : "nread longer than length"; 134 | assert parser.body_start <= len : "body starts after buffer end"; 135 | assert parser.mark < len : "mark is after buffer end"; 136 | assert parser.field_len <= len : "field has length longer than whole buffer"; 137 | assert parser.field_start < len : "field starts after buffer end"; 138 | 139 | return parser.nread; 140 | } 141 | 142 | public int finish() { 143 | if(has_error()) { 144 | return -1; 145 | } else if(is_finished()) { 146 | return 1; 147 | } else { 148 | return 0; 149 | } 150 | } 151 | 152 | public boolean has_error() { 153 | return parser.cs == http_parser_error; 154 | } 155 | 156 | public boolean is_finished() { 157 | return parser.cs == http_parser_first_final; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /ext/http11/http11_parser.rl: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2005 Zed A. Shaw 3 | * You can redistribute it and/or modify it under the same terms as Ruby. 4 | */ 5 | #include "http11_parser.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * capitalizes all lower-case ASCII characters, 14 | * converts dashes to underscores. 15 | */ 16 | static void snake_upcase_char(char *c) 17 | { 18 | if (*c >= 'a' && *c <= 'z') 19 | *c &= ~0x20; 20 | else if (*c == '-') 21 | *c = '_'; 22 | } 23 | 24 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 25 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 26 | #define PTR_TO(F) (buffer + parser->F) 27 | 28 | /** Machine **/ 29 | 30 | %%{ 31 | 32 | machine http_parser; 33 | 34 | action mark {MARK(mark, fpc); } 35 | 36 | 37 | action start_field { MARK(field_start, fpc); } 38 | action snake_upcase_field { snake_upcase_char((char *)fpc); } 39 | action write_field { 40 | parser->field_len = LEN(field_start, fpc); 41 | } 42 | 43 | action start_value { MARK(mark, fpc); } 44 | action write_value { 45 | if(parser->http_field != NULL) { 46 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); 47 | } 48 | } 49 | action request_method { 50 | if(parser->request_method != NULL) 51 | parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc)); 52 | } 53 | action request_uri { 54 | if(parser->request_uri != NULL) 55 | parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc)); 56 | } 57 | action fragment { 58 | if(parser->fragment != NULL) 59 | parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc)); 60 | } 61 | 62 | action start_query {MARK(query_start, fpc); } 63 | action query_string { 64 | if(parser->query_string != NULL) 65 | parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc)); 66 | } 67 | 68 | action http_version { 69 | if(parser->http_version != NULL) 70 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); 71 | } 72 | 73 | action request_path { 74 | if(parser->request_path != NULL) 75 | parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc)); 76 | } 77 | 78 | action done { 79 | parser->body_start = fpc - buffer + 1; 80 | if(parser->header_done != NULL) 81 | parser->header_done(parser->data, fpc + 1, pe - fpc - 1); 82 | fbreak; 83 | } 84 | 85 | include http_parser_common "http11_parser_common.rl"; 86 | 87 | }%% 88 | 89 | /** Data **/ 90 | %% write data; 91 | 92 | int http_parser_init(http_parser *parser) { 93 | int cs = 0; 94 | %% write init; 95 | parser->cs = cs; 96 | parser->body_start = 0; 97 | parser->content_len = 0; 98 | parser->mark = 0; 99 | parser->nread = 0; 100 | parser->field_len = 0; 101 | parser->field_start = 0; 102 | 103 | return(1); 104 | } 105 | 106 | 107 | /** exec **/ 108 | size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) { 109 | const char *p, *pe; 110 | int cs = parser->cs; 111 | 112 | assert(off <= len && "offset past end of buffer"); 113 | 114 | p = buffer+off; 115 | pe = buffer+len; 116 | 117 | /* assert(*pe == '\0' && "pointer does not end on NUL"); */ 118 | assert(pe - p == len - off && "pointers aren't same distance"); 119 | 120 | %% write exec; 121 | 122 | if (!http_parser_has_error(parser)) 123 | parser->cs = cs; 124 | parser->nread += p - (buffer + off); 125 | 126 | assert(p <= pe && "buffer overflow after parsing execute"); 127 | assert(parser->nread <= len && "nread longer than length"); 128 | assert(parser->body_start <= len && "body starts after buffer end"); 129 | assert(parser->mark < len && "mark is after buffer end"); 130 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 131 | assert(parser->field_start < len && "field starts after buffer end"); 132 | 133 | return(parser->nread); 134 | } 135 | 136 | int http_parser_finish(http_parser *parser) 137 | { 138 | if (http_parser_has_error(parser) ) { 139 | return -1; 140 | } else if (http_parser_is_finished(parser) ) { 141 | return 1; 142 | } else { 143 | return 0; 144 | } 145 | } 146 | 147 | int http_parser_has_error(http_parser *parser) { 148 | return parser->cs == http_parser_error; 149 | } 150 | 151 | int http_parser_is_finished(http_parser *parser) { 152 | return parser->cs >= http_parser_first_final; 153 | } 154 | -------------------------------------------------------------------------------- /ext/http11/http11_parser_common.rl: -------------------------------------------------------------------------------- 1 | %%{ 2 | 3 | machine http_parser_common; 4 | 5 | #### HTTP PROTOCOL GRAMMAR 6 | # line endings 7 | CRLF = "\r\n"; 8 | 9 | # character types 10 | CTL = (cntrl | 127); 11 | safe = ("$" | "-" | "_" | "."); 12 | extra = ("!" | "*" | "'" | "(" | ")" | ","); 13 | reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); 14 | unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); 15 | national = any -- (alpha | digit | reserved | extra | safe | unsafe); 16 | unreserved = (alpha | digit | safe | extra | national); 17 | escape = ("%" xdigit xdigit); 18 | uchar = (unreserved | escape); 19 | pchar = (uchar | ":" | "@" | "&" | "=" | "+"); 20 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 21 | 22 | # elements 23 | token = (ascii -- (CTL | tspecials)); 24 | 25 | # URI schemes and absolute paths 26 | scheme = ( alpha | digit | "+" | "-" | "." )* ; 27 | absolute_uri = (scheme ":" (uchar | reserved )*); 28 | 29 | path = ( pchar+ ( "/" pchar* )* ) ; 30 | query = ( uchar | reserved )* %query_string ; 31 | param = ( pchar | "/" )* ; 32 | params = ( param ( ";" param )* ) ; 33 | rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?; 34 | absolute_path = ( "/"+ rel_path ); 35 | 36 | Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri; 37 | Fragment = ( uchar | reserved )* >mark %fragment; 38 | Method = ( upper | digit | safe ){1,20} >mark %request_method; 39 | 40 | http_number = ( digit+ "." digit+ ) ; 41 | HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ; 42 | Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ; 43 | 44 | field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field; 45 | 46 | field_value = any* >start_value %write_value; 47 | 48 | message_header = field_name ":" " "* field_value :> CRLF; 49 | 50 | Request = Request_Line ( message_header )* ( CRLF @done ); 51 | 52 | main := Request; 53 | 54 | }%% 55 | -------------------------------------------------------------------------------- /lib/mongrel/camping.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'mongrel' 8 | 9 | 10 | module Mongrel 11 | # Support for the Camping micro framework at http://camping.rubyforge.org 12 | # This implements the unusually long Postamble that Camping usually 13 | # needs and shrinks it down to just a single line or two. 14 | # 15 | # Your Postamble would now be: 16 | # 17 | # Mongrel::Camping::start("0.0.0.0",3001,"/tepee",Tepee).join 18 | # 19 | # If you wish to get fancier than this then you can use the 20 | # Camping::CampingHandler directly instead and do your own 21 | # wiring: 22 | # 23 | # h = Mongrel::HttpServer.new(server, port) 24 | # h.register(uri, CampingHandler.new(Tepee)) 25 | # h.register("/favicon.ico", Mongrel::Error404Handler.new("")) 26 | # 27 | # I add the /favicon.ico since camping apps typically don't 28 | # have them and it's just annoying anyway. 29 | module Camping 30 | 31 | # This is a specialized handler for Camping applications 32 | # that has them process the request and then translates 33 | # the results into something the Mongrel::HttpResponse 34 | # needs. 35 | class CampingHandler < Mongrel::HttpHandler 36 | attr_reader :files 37 | attr_reader :guard 38 | @@file_only_methods = ["GET","HEAD"] 39 | 40 | def initialize(klass) 41 | @files = Mongrel::DirHandler.new(nil, false) 42 | @guard = Mutex.new 43 | @klass = klass 44 | end 45 | 46 | def process(request, response) 47 | if response.socket.closed? 48 | return 49 | end 50 | 51 | controller = nil 52 | @guard.synchronize { 53 | controller = @klass.run(request.body, request.params) 54 | } 55 | 56 | sendfile, clength = nil 57 | response.status = controller.status 58 | controller.headers.each do |k, v| 59 | if k =~ /^X-SENDFILE$/i 60 | sendfile = v 61 | elsif k =~ /^CONTENT-LENGTH$/i 62 | clength = v.to_i 63 | else 64 | [*v].each do |vi| 65 | response.header[k] = vi 66 | end 67 | end 68 | end 69 | 70 | if sendfile 71 | request.params[Mongrel::Const::PATH_INFO] = sendfile 72 | @files.process(request, response) 73 | elsif controller.body.respond_to? :read 74 | response.send_status(clength) 75 | response.send_header 76 | while chunk = controller.body.read(16384) 77 | response.write(chunk) 78 | end 79 | if controller.body.respond_to? :close 80 | controller.body.close 81 | end 82 | else 83 | body = controller.body.to_s 84 | response.send_status(body.length) 85 | response.send_header 86 | response.write(body) 87 | end 88 | end 89 | end 90 | 91 | # This is a convenience method that wires up a CampingHandler 92 | # for your application on a given port and uri. It's pretty 93 | # much all you need for a camping application to work right. 94 | # 95 | # It returns the Mongrel::HttpServer which you should either 96 | # join or somehow manage. The thread is running when 97 | # returned. 98 | 99 | def Camping.start(server, port, uri, klass) 100 | h = Mongrel::HttpServer.new(server, port) 101 | h.register(uri, CampingHandler.new(klass)) 102 | h.register("/favicon.ico", Mongrel::Error404Handler.new("")) 103 | h.run 104 | return h 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/mongrel/const.rb: -------------------------------------------------------------------------------- 1 | 2 | module Mongrel 3 | 4 | # Every standard HTTP code mapped to the appropriate message. These are 5 | # used so frequently that they are placed directly in Mongrel for easy 6 | # access rather than Mongrel::Const itself. 7 | HTTP_STATUS_CODES = { 8 | 100 => 'Continue', 9 | 101 => 'Switching Protocols', 10 | 200 => 'OK', 11 | 201 => 'Created', 12 | 202 => 'Accepted', 13 | 203 => 'Non-Authoritative Information', 14 | 204 => 'No Content', 15 | 205 => 'Reset Content', 16 | 206 => 'Partial Content', 17 | 300 => 'Multiple Choices', 18 | 301 => 'Moved Permanently', 19 | 302 => 'Moved Temporarily', 20 | 303 => 'See Other', 21 | 304 => 'Not Modified', 22 | 305 => 'Use Proxy', 23 | 400 => 'Bad Request', 24 | 401 => 'Unauthorized', 25 | 402 => 'Payment Required', 26 | 403 => 'Forbidden', 27 | 404 => 'Not Found', 28 | 405 => 'Method Not Allowed', 29 | 406 => 'Not Acceptable', 30 | 407 => 'Proxy Authentication Required', 31 | 408 => 'Request Time-out', 32 | 409 => 'Conflict', 33 | 410 => 'Gone', 34 | 411 => 'Length Required', 35 | 412 => 'Precondition Failed', 36 | 413 => 'Request Entity Too Large', 37 | 414 => 'Request-URI Too Large', 38 | 415 => 'Unsupported Media Type', 39 | 500 => 'Internal Server Error', 40 | 501 => 'Not Implemented', 41 | 502 => 'Bad Gateway', 42 | 503 => 'Service Unavailable', 43 | 504 => 'Gateway Time-out', 44 | 505 => 'HTTP Version not supported' 45 | } 46 | 47 | # Frequently used constants when constructing requests or responses. Many times 48 | # the constant just refers to a string with the same contents. Using these constants 49 | # gave about a 3% to 10% performance improvement over using the strings directly. 50 | # Symbols did not really improve things much compared to constants. 51 | # 52 | # While Mongrel does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT, 53 | # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or 54 | # too taxing on performance. 55 | module Const 56 | DATE = "Date".freeze 57 | 58 | # This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this. 59 | PATH_INFO="PATH_INFO".freeze 60 | 61 | # This is the initial part that your handler is identified as by URIClassifier. 62 | SCRIPT_NAME="SCRIPT_NAME".freeze 63 | 64 | # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME. 65 | REQUEST_URI='REQUEST_URI'.freeze 66 | REQUEST_PATH='REQUEST_PATH'.freeze 67 | 68 | MONGREL_VERSION = VERSION = "1.2.0.beta.1".freeze 69 | 70 | MONGREL_TMP_BASE="mongrel".freeze 71 | 72 | # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff. 73 | ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze 74 | 75 | CONTENT_LENGTH="CONTENT_LENGTH".freeze 76 | 77 | # A common header for indicating the server is too busy. Not used yet. 78 | ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze 79 | 80 | # The basic max request size we'll try to read. 81 | CHUNK_SIZE=(16 * 1024) 82 | 83 | # This is the maximum header that is allowed before a client is booted. The parser detects 84 | # this, but we'd also like to do this as well. 85 | MAX_HEADER=1024 * (80 + 32) 86 | 87 | # Maximum request body size before it is moved out of memory and into a tempfile for reading. 88 | MAX_BODY=MAX_HEADER 89 | 90 | # A frozen format for this is about 15% faster 91 | STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n".freeze 92 | CONTENT_TYPE = "Content-Type".freeze 93 | LAST_MODIFIED = "Last-Modified".freeze 94 | ETAG = "ETag".freeze 95 | SLASH = "/".freeze 96 | REQUEST_METHOD="REQUEST_METHOD".freeze 97 | GET="GET".freeze 98 | HEAD="HEAD".freeze 99 | # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32) 100 | ETAG_FORMAT="\"%x-%x-%x\"".freeze 101 | HEADER_FORMAT="%s: %s\r\n".freeze 102 | LINE_END="\r\n".freeze 103 | REMOTE_ADDR="REMOTE_ADDR".freeze 104 | HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze 105 | HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE".freeze 106 | HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze 107 | REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze 108 | HOST = "HOST".freeze 109 | end 110 | end -------------------------------------------------------------------------------- /lib/mongrel/gems.rb: -------------------------------------------------------------------------------- 1 | module Mongrel 2 | module Gems 3 | class << self 4 | 5 | def require(library, version = nil) 6 | begin 7 | Kernel.require library 8 | rescue LoadError, RuntimeError => e 9 | begin 10 | # ActiveSupport breaks 'require' by making it always return a true value 11 | Kernel.require 'rubygems' 12 | version ? gem(library, version) : gem(library) 13 | retry 14 | rescue Gem::LoadError, LoadError, RuntimeError 15 | # puts "** #{library.inspect} could not be loaded" unless library == "mongrel_experimental" 16 | end 17 | end 18 | end 19 | 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/mongrel/header_out.rb: -------------------------------------------------------------------------------- 1 | module Mongrel 2 | # This class implements a simple way of constructing the HTTP headers dynamically 3 | # via a Hash syntax. Think of it as a write-only Hash. Refer to HttpResponse for 4 | # information on how this is used. 5 | # 6 | # One consequence of this write-only nature is that you can write multiple headers 7 | # by just doing them twice (which is sometimes needed in HTTP), but that the normal 8 | # semantics for Hash (where doing an insert replaces) is not there. 9 | class HeaderOut 10 | attr_reader :out 11 | attr_accessor :allowed_duplicates 12 | 13 | def initialize(out) 14 | @sent = {} 15 | @allowed_duplicates = {"Set-Cookie" => true, "Set-Cookie2" => true, 16 | "Warning" => true, "WWW-Authenticate" => true} 17 | @out = out 18 | end 19 | 20 | # Simply writes "#{key}: #{value}" to an output buffer. 21 | def[]=(key,value) 22 | if not @sent.has_key?(key) or @allowed_duplicates.has_key?(key) 23 | @sent[key] = true 24 | @out.write(Const::HEADER_FORMAT % [key, value]) 25 | end 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /lib/mongrel/init.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'mongrel/gems' 8 | Mongrel::Gems.require 'gem_plugin' 9 | 10 | # File is just a stub that makes sure the mongrel_plugins gem is loaded and ready 11 | -------------------------------------------------------------------------------- /lib/mongrel/stats.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | # A very simple little class for doing some basic fast statistics sampling. 8 | # You feed it either samples of numeric data you want measured or you call 9 | # Stats.tick to get it to add a time delta between the last time you called it. 10 | # When you're done either call sum, sumsq, n, min, max, mean or sd to get 11 | # the information. The other option is to just call dump and see everything. 12 | # 13 | # It does all of this very fast and doesn't take up any memory since the samples 14 | # are not stored but instead all the values are calculated on the fly. 15 | module Mongrel 16 | class Stats 17 | attr_reader :sum, :sumsq, :n, :min, :max 18 | 19 | def initialize(name) 20 | @name = name 21 | reset 22 | end 23 | 24 | # Resets the internal counters so you can start sampling again. 25 | def reset 26 | @sum = 0.0 27 | @sumsq = 0.0 28 | @last_time = Time.new 29 | @n = 0.0 30 | @min = 0.0 31 | @max = 0.0 32 | end 33 | 34 | # Adds a sampling to the calculations. 35 | def sample(s) 36 | @sum += s 37 | @sumsq += s * s 38 | if @n == 0 39 | @min = @max = s 40 | else 41 | @min = s if @min > s 42 | @max = s if @max < s 43 | end 44 | @n+=1 45 | end 46 | 47 | # Dump this Stats object with an optional additional message. 48 | def dump(msg = "", out=STDERR) 49 | out.puts "#{msg}: #{self.to_s}" 50 | end 51 | 52 | # Returns a common display (used by dump) 53 | def to_s 54 | "[#{@name}]: SUM=%0.4f, SUMSQ=%0.4f, N=%0.4f, MEAN=%0.4f, SD=%0.4f, MIN=%0.4f, MAX=%0.4f" % [@sum, @sumsq, @n, mean, sd, @min, @max] 55 | end 56 | 57 | 58 | # Calculates and returns the mean for the data passed so far. 59 | def mean 60 | @sum / @n 61 | end 62 | 63 | # Calculates the standard deviation of the data so far. 64 | def sd 65 | # (sqrt( ((s).sumsq - ( (s).sum * (s).sum / (s).n)) / ((s).n-1) )) 66 | begin 67 | return Math.sqrt( (@sumsq - ( @sum * @sum / @n)) / (@n-1) ) 68 | rescue Errno::EDOM 69 | return 0.0 70 | end 71 | end 72 | 73 | 74 | # Adds a time delta between now and the last time you called this. This 75 | # will give you the average time between two activities. 76 | # 77 | # An example is: 78 | # 79 | # t = Stats.new("do_stuff") 80 | # 10000.times { do_stuff(); t.tick } 81 | # t.dump("time") 82 | # 83 | def tick 84 | now = Time.now 85 | sample(now - @last_time) 86 | @last_time = now 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/mongrel/tcphack.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | 8 | # A modification proposed by Sean Treadway that increases the default accept 9 | # queue of TCPServer to 1024 so that it handles more concurrent requests. 10 | class TCPServer 11 | def initialize_with_backlog(*args) 12 | initialize_without_backlog(*args) 13 | listen(1024) 14 | end 15 | 16 | alias_method :initialize_without_backlog, :initialize 17 | alias_method :initialize, :initialize_with_backlog 18 | end 19 | -------------------------------------------------------------------------------- /lib/mongrel/uri_classifier.rb: -------------------------------------------------------------------------------- 1 | 2 | module Mongrel 3 | class URIClassifier 4 | 5 | class RegistrationError < RuntimeError 6 | end 7 | class UsageError < RuntimeError 8 | end 9 | 10 | attr_reader :handler_map 11 | 12 | # Returns the URIs that have been registered with this classifier so far. 13 | def uris 14 | @handler_map.keys 15 | end 16 | 17 | def initialize 18 | @handler_map = {} 19 | @matcher = // 20 | @root_handler = nil 21 | end 22 | 23 | # Register a handler object at a particular URI. The handler can be whatever 24 | # you want, including an array. It's up to you what to do with it. 25 | # 26 | # Registering a handler is not necessarily threadsafe, so be careful if you go 27 | # mucking around once the server is running. 28 | def register(uri, handler) 29 | raise RegistrationError, "#{uri.inspect} is already registered" if @handler_map[uri] 30 | raise RegistrationError, "URI is empty" if !uri or uri.empty? 31 | raise RegistrationError, "URI must begin with a \"#{Const::SLASH}\"" unless uri[0..0] == Const::SLASH 32 | @handler_map[uri.dup] = handler 33 | rebuild 34 | end 35 | 36 | # Unregister a particular URI and its handler. 37 | def unregister(uri) 38 | handler = @handler_map.delete(uri) 39 | raise RegistrationError, "#{uri.inspect} was not registered" unless handler 40 | rebuild 41 | handler 42 | end 43 | 44 | # Resolve a request URI by finding the best partial match in the registered 45 | # handler URIs. 46 | def resolve(request_uri) 47 | if @root_handler 48 | # Optimization for the pathological case of only one handler on "/"; e.g. Rails 49 | [Const::SLASH, request_uri, @root_handler] 50 | elsif match = @matcher.match(request_uri) 51 | uri = match.to_s 52 | # A root mounted ("/") handler must resolve such that path info matches the original URI. 53 | [uri, (uri == Const::SLASH ? request_uri : match.post_match), @handler_map[uri]] 54 | else 55 | [nil, nil, nil] 56 | end 57 | end 58 | 59 | private 60 | 61 | def rebuild 62 | if @handler_map.size == 1 and @handler_map[Const::SLASH] 63 | @root_handler = @handler_map.values.first 64 | else 65 | @root_handler = nil 66 | routes = @handler_map.keys.sort.sort_by do |uri| 67 | -uri.length 68 | end 69 | @matcher = Regexp.new(routes.map do |uri| 70 | Regexp.new('^' + Regexp.escape(uri)) 71 | end.join('|')) 72 | end 73 | end 74 | 75 | end 76 | end -------------------------------------------------------------------------------- /mongrel-public_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUDCCAjigAwIBAgIBADANBgkqhkiG9w0BAQUFADBOMRwwGgYDVQQDDBNtb25n 3 | cmVsLWRldmVsb3BtZW50MRkwFwYKCZImiZPyLGQBGRYJcnVieWZvcmdlMRMwEQYK 4 | CZImiZPyLGQBGRYDb3JnMB4XDTA3MDkxNjEwMzI0OVoXDTA4MDkxNTEwMzI0OVow 5 | TjEcMBoGA1UEAwwTbW9uZ3JlbC1kZXZlbG9wbWVudDEZMBcGCgmSJomT8ixkARkW 6 | CXJ1Ynlmb3JnZTETMBEGCgmSJomT8ixkARkWA29yZzCCASIwDQYJKoZIhvcNAQEB 7 | BQADggEPADCCAQoCggEBAMb9v3B01eOHk3FyypbQgKXzJplUE5P6dXoG+xpPm0Lv 8 | P7BQmeMncOwqQ7zXpVQU+lTpXtQFTsOE3vL7KnhQFJKGvUAkbh24VFyopu1I0yqF 9 | mGu4nRqNXGXVj8TvLSj4S1WpSRLAa0acLPNyKhGmoV9+crqQypSjM6XKjBeppifo 10 | 4eBmWGjiJEYMIJBvJZPJ4rAVDDA8C6CM1m3gMBGNh8ELDhU8HI9AP3dMIkTI2Wx9 11 | 9xkJwHdroAaS0IFFtYChrwee4FbCF1FHDgoTosMwa47DrLHg4hZ6ojaKwK5QVWEV 12 | XGb6ju5UqpktnSWF2W+Lvl/K0tI42OH2CAhebT1gEVUCAwEAAaM5MDcwCQYDVR0T 13 | BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFGHChyMSZ16u9WOzKhgJSQ9lqDc5 14 | MA0GCSqGSIb3DQEBBQUAA4IBAQA/lfeN2WdB1xN+82tT7vNS4HOjRQw6MUh5yktu 15 | GQjaGqm0UB+aX0Z9y0B0qpfv9rj7nmIvEGiwBmDepNWYCGuW15JyqpN7QVVnG2xS 16 | Mrame7VqgjM7A+VGDD5In5LtWbM/CHAATvvFlQ5Ph13YE1EdnVbZ65c+KQv+5sFY 17 | Q+zEop74d878uaC/SAHHXS46TiXneocaLSYw1CEZs/MAIy+9c4Q5ESbGpgnfg1Ad 18 | 6lwl7k3hsNHO/+tZzx4HJtOXDI1yAl3+q6T9J0yI3z97EinwvAKhS1eyOI2Y5eeT 19 | tbQaNYkU127B3l/VNpd8fQm3Jkl/PqCCmDBQjUszFrJEODug 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v2.5.0. Not required for JRuby. 3 | 4 | v2.4.0. Signed gem. 5 | 6 | v2.3.0. Use STDERR, not $stderr, just like Mongrel; tests now use Test::Unit; moving to the mongrel project on RubyForge. 7 | 8 | v2.2.0. Don't load on Ruby > 1.8.5; copyright correction. 9 | 10 | v2.1.0. License change due to no provision for use in original Ruby license (prevents installation in Florida). 11 | 12 | v2.0.0. Updated for second cgi.rb vulnerability. 13 | 14 | v1.0.0. Original single-patch release by Zed Shaw, et. al. 15 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/Manifest: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | lib/cgi_multipart_eof_fix.rb 3 | LICENSE 4 | Manifest 5 | README 6 | test/test_cgi_multipart_eof_fix.rb 7 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/README: -------------------------------------------------------------------------------- 1 | 2 | cgi_multipart_eof_fix 3 | 4 | Fix an exploitable bug in CGI multipart parsing. 5 | 6 | == License 7 | 8 | Copyright 2006, 2007 Cloudburst, LLC. Portions copyright 2006 Jeremy Kemper, Jamis Buck, Zed A. Shaw, and Yukihiro Matsumoto, and used with permission. See the included LICENSE file. 9 | 10 | == Description 11 | 12 | Fixes an exploitable bug in CGI multipart parsing which affects Ruby <= 1.8.5. When multipart boundary attributes contain non-halting regular expression strings, the boundary searcher in the CGI module does not properly escape the parameter and will execute arbitrary regular expressions. This fix adds escaping for the user data. 13 | 14 | * Affected application servers: standalone CGI, Mongrel, WEBrick 15 | * Unaffected: FastCGI, Ruby 1.8.6 (all servers) 16 | * Unknown: mod_ruby 17 | 18 | This fix will not modify versions of Ruby greater than 1.8.5, and is cumulative with previous CGI multipart vulnerability fixes. 19 | 20 | == Usage 21 | 22 | Install the gem: 23 | sudo gem install cgi_multipart_eof_fix 24 | 25 | Run the included test to verify that the patch works as intended. Then, require the gem in every affected application, as follows: 26 | 27 | require 'rubygems' 28 | require 'cgi_multipart_eof_fix' 29 | 30 | Currently mongrel_rails requires this gem automatically. However, Mongrel may change in the future. 31 | 32 | == Reporting problems 33 | 34 | * http://rubyforge.org/tracker/?group_id=1306 35 | 36 | == Further resources 37 | 38 | * http://rubyforge.org/mailman/listinfo/mongrel-users 39 | * http://blog.evanweaver.com/articles/2006/12/05/cgi-rb-vulnerability-hotfix 40 | * http://www.ruby-lang.org/en/news/2006/12/04/another-dos-vulnerability-in-cgi-library/ 41 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("cgi_multipart_eof_fix") do |p| 5 | p.author = "Evan Weaver" 6 | p.rubyforge_name = "mongrel" 7 | p.summary = p.description = "Fix an exploitable bug in CGI multipart parsing." 8 | p.url = "http://blog.evanweaver.com/pages/code#cgi_multipart_eof_fix" 9 | p.docs_host = "evan.github.com/fauna/" 10 | p.rdoc_pattern = /CHANGELOG|LICENSE|README|lib\/cgi_multipart_eof_fix/ 11 | 12 | p.need_tar_gz = false 13 | p.need_tgz = true 14 | p.certificate_chain = ['/Users/eweaver/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 15 | '/Users/eweaver/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 16 | p.require_signed = true 17 | end 18 | 19 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/lib/cgi_multipart_eof_fix.rb: -------------------------------------------------------------------------------- 1 | 2 | # unfortunately: 3 | # >> "1.8.6" < "1.8.10" 4 | # => false 5 | 6 | version = RUBY_VERSION.split(".").map {|i| i.to_i } 7 | 8 | if version [0] < 2 and version [1] < 9 and version [2] < 6 and RUBY_PLATFORM !~ /java/ 9 | 10 | STDERR.puts "** Ruby version is not up-to-date; loading cgi_multipart_eof_fix" 11 | 12 | require 'cgi' 13 | 14 | class CGI 15 | module QueryExtension 16 | def read_multipart(boundary, content_length) 17 | params = Hash.new([]) 18 | boundary = "--" + boundary 19 | quoted_boundary = Regexp.quote(boundary, "n") 20 | buf = "" 21 | bufsize = 10 * 1024 22 | boundary_end="" 23 | 24 | # start multipart/form-data 25 | stdinput.binmode if defined? stdinput.binmode 26 | boundary_size = boundary.size + EOL.size 27 | content_length -= boundary_size 28 | status = stdinput.read(boundary_size) 29 | if nil == status 30 | raise EOFError, "no content body" 31 | elsif boundary + EOL != status 32 | raise EOFError, "bad content body #{status.inspect} expected, got #{(boundary + EOL).inspect}" 33 | end 34 | 35 | loop do 36 | head = nil 37 | if 10240 < content_length 38 | require "tempfile" 39 | body = Tempfile.new("CGI") 40 | else 41 | begin 42 | require "stringio" 43 | body = StringIO.new 44 | rescue LoadError 45 | require "tempfile" 46 | body = Tempfile.new("CGI") 47 | end 48 | end 49 | body.binmode if defined? body.binmode 50 | 51 | until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) 52 | 53 | if (not head) and /#{EOL}#{EOL}/n.match(buf) 54 | buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do 55 | head = $1.dup 56 | "" 57 | end 58 | next 59 | end 60 | 61 | if head and ( (EOL + boundary + EOL).size < buf.size ) 62 | body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] 63 | buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" 64 | end 65 | 66 | c = if bufsize < content_length 67 | stdinput.read(bufsize) 68 | else 69 | stdinput.read(content_length) 70 | end 71 | if c.nil? || c.empty? 72 | raise EOFError, "bad content body" 73 | end 74 | buf.concat(c) 75 | content_length -= c.size 76 | end 77 | 78 | buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do 79 | body.print $1 80 | if "--" == $2 81 | content_length = -1 82 | end 83 | boundary_end = $2.dup 84 | "" 85 | end 86 | 87 | body.rewind 88 | 89 | /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head) 90 | filename = ($1 or "") 91 | if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and 92 | /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and 93 | (not /MSIE/ni.match(env_table['HTTP_USER_AGENT'])) 94 | filename = CGI::unescape(filename) 95 | end 96 | 97 | /Content-Type: (.*)/ni.match(head) 98 | content_type = ($1 or "") 99 | 100 | (class << body; self; end).class_eval do 101 | alias local_path path 102 | define_method(:original_filename) {filename.dup.taint} 103 | define_method(:content_type) {content_type.dup.taint} 104 | end 105 | 106 | /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head) 107 | name = $1.dup 108 | 109 | if params.has_key?(name) 110 | params[name].push(body) 111 | else 112 | params[name] = [body] 113 | end 114 | break if buf.size == 0 115 | break if content_length === -1 116 | end 117 | raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ 118 | 119 | params 120 | end # read_multipart 121 | private :read_multipart 122 | end 123 | end 124 | 125 | else 126 | # Ruby version is up-to-date; cgi_multipart_eof_fix was not loaded 127 | end 128 | -------------------------------------------------------------------------------- /projects/cgi_multipart_eof_fix/test/test_cgi_multipart_eof_fix.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'cgi' 5 | require 'stringio' 6 | require 'timeout' 7 | 8 | BOUNDARY = '%?%(\w*)\\((\w*)\\)' 9 | PAYLOAD = "--#{BOUNDARY}\r\nContent-Disposition: form-data; name=\"a_field\"\r\n\r\nBang!\r\n--#{BOUNDARY}--\r\n" 10 | ENV['REQUEST_METHOD'] = "POST" 11 | ENV['CONTENT_TYPE'] = "multipart/form-data; boundary=\"#{BOUNDARY}\"" 12 | ENV['CONTENT_LENGTH'] = PAYLOAD.length.to_s 13 | 14 | Object.send(:remove_const, :STDERR) 15 | STDERR = StringIO.new # hide the multipart load warnings 16 | 17 | version = RUBY_VERSION.split(".").map {|i| i.to_i } 18 | IS_VULNERABLE = (version [0] < 2 and version [1] < 9 and version [2] < 6 and RUBY_PLATFORM !~ /java/) 19 | 20 | class CgiMultipartTestError < StandardError 21 | end 22 | 23 | class CgiMultipartEofFixTest < Test::Unit::TestCase 24 | 25 | def read_multipart 26 | # can't use STDIN because of the dynamic constant assignment rule 27 | $stdin = StringIO.new(PAYLOAD) 28 | 29 | begin 30 | Timeout.timeout(3) do 31 | CGI.new 32 | end 33 | "CGI is safe: read_multipart does not hang on malicious multipart requests." 34 | rescue TimeoutError 35 | raise CgiMultipartTestError, "CGI is exploitable: read_multipart hangs on malicious multipart requests." 36 | end 37 | end 38 | 39 | def test_exploitable 40 | if IS_VULNERABLE 41 | assert_raises CgiMultipartTestError do 42 | read_multipart 43 | end 44 | else 45 | # we're on 1.8.6 or higher already 46 | assert_nothing_raised do 47 | read_multipart 48 | end 49 | end 50 | end 51 | 52 | def test_fixed 53 | assert_nothing_raised do 54 | load "#{File.dirname(__FILE__)}/../lib/cgi_multipart_eof_fix.rb" 55 | read_multipart 56 | end 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /projects/fastthread/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v1.0.1. Signed gem. 3 | -------------------------------------------------------------------------------- /projects/fastthread/Manifest: -------------------------------------------------------------------------------- 1 | test/test_queue.rb 2 | test/test_mutex.rb 3 | test/test_condvar.rb 4 | test/test_all.rb 5 | setup.rb 6 | Manifest 7 | ext/fastthread/fastthread.c 8 | ext/fastthread/extconf.rb 9 | CHANGELOG 10 | -------------------------------------------------------------------------------- /projects/fastthread/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("fastthread") do |p| 5 | p.project = "mongrel" 6 | p.author = "MenTaLguY " 7 | p.summary = "Optimized replacement for thread.rb primitives" 8 | p.extensions = "ext/fastthread/extconf.rb" 9 | p.clean_pattern = ['build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log', "ext/fastthread/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/fastthread/Makefile", "pkg", "lib/*.bundle", "*.gem", ".config"] 10 | 11 | p.need_tar_gz = false 12 | p.need_tgz = true 13 | p.certificate_chain = ['/Users/eweaver/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 14 | '/Users/eweaver/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 15 | p.require_signed = true 16 | 17 | p.eval = proc do 18 | if RUBY_PLATFORM.match("win32") 19 | platform = Gem::Platform::WIN32 20 | files += ['lib/fastthread.so'] 21 | task :package => [:clean, :compile] 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /projects/fastthread/ext/fastthread/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | Makefile 3 | -------------------------------------------------------------------------------- /projects/fastthread/ext/fastthread/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | create_makefile('fastthread') 4 | -------------------------------------------------------------------------------- /projects/fastthread/test/test_all.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | $:.unshift File.expand_path( File.dirname( __FILE__ ) ) 3 | require 'test_mutex' 4 | require 'test_condvar' 5 | require 'test_queue' 6 | 7 | -------------------------------------------------------------------------------- /projects/fastthread/test/test_condvar.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'thread' 3 | if RUBY_PLATFORM != "java" 4 | $:.unshift File.expand_path( File.join( File.dirname( __FILE__ ), "../ext/fastthread" ) ) 5 | require 'fastthread' 6 | end 7 | 8 | class TestCondVar < Test::Unit::TestCase 9 | def test_signal 10 | s = "" 11 | m = Mutex.new 12 | cv = ConditionVariable.new 13 | ready = false 14 | 15 | t = Thread.new do 16 | nil until ( Thread.pass ; m.synchronize { ready } ) 17 | m.synchronize { s << "b" } 18 | cv.signal 19 | end 20 | 21 | m.synchronize do 22 | s << "a" 23 | ready = true 24 | cv.wait m 25 | assert m.locked? 26 | s << "c" 27 | end 28 | 29 | t.join 30 | 31 | assert_equal "abc", s 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /projects/fastthread/test/test_mutex.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'thread' 3 | if RUBY_PLATFORM != "java" 4 | $:.unshift File.expand_path( File.join( File.dirname( __FILE__ ), "../ext/fastthread" ) ) 5 | require 'fastthread' 6 | end 7 | 8 | class TestMutex < Test::Unit::TestCase 9 | def self.mutex_test( name, &body ) 10 | define_method( "test_#{ name }" ) do 11 | body.call( self, Mutex.new, "" ) 12 | end 13 | # at one time we also tested Mutex_m here, but it's no longer 14 | # part of fastthread 15 | end 16 | 17 | mutex_test( :locked_p ) do |test, m, prefix| 18 | test.instance_eval do 19 | assert_equal false, m.send( "#{ prefix }locked?" ) 20 | m.send "#{ prefix }lock" 21 | assert_equal true, m.send( "#{ prefix }locked?" ) 22 | m.send "#{ prefix }unlock" 23 | assert_equal false, m.send( "#{ prefix }locked?" ) 24 | end 25 | end 26 | 27 | mutex_test( :synchronize ) do |test, m, prefix| 28 | test.instance_eval do 29 | assert !m.send( "#{ prefix }locked?" ) 30 | m.send "#{ prefix }synchronize" do 31 | assert m.send( "#{ prefix }locked?" ) 32 | end 33 | assert !m.send( "#{ prefix }locked?" ) 34 | end 35 | end 36 | 37 | mutex_test( :synchronize_exception ) do |test, m, prefix| 38 | test.instance_eval do 39 | assert !m.send( "#{ prefix }locked?" ) 40 | assert_raise ArgumentError do 41 | m.send "#{ prefix }synchronize" do 42 | assert m.send( "#{ prefix }locked?" ) 43 | raise ArgumentError 44 | end 45 | end 46 | assert !m.send( "#{ prefix }locked?" ) 47 | end 48 | end 49 | 50 | mutex_test( :mutual_exclusion ) do |test, m, prefix| 51 | test.instance_eval do 52 | s = "" 53 | 54 | ("a".."c").map do |c| 55 | Thread.new do 56 | Thread.pass 57 | 5.times do 58 | m.send "#{ prefix }synchronize" do 59 | s << c 60 | Thread.pass 61 | s << c 62 | end 63 | end 64 | end 65 | end.each do |thread| 66 | thread.join 67 | end 68 | 69 | assert_equal 30, s.length 70 | assert s.match( /^(aa|bb|cc)+$/ ) 71 | end 72 | end 73 | end 74 | 75 | -------------------------------------------------------------------------------- /projects/fastthread/test/test_queue.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'thread' 3 | if RUBY_PLATFORM != "java" 4 | $:.unshift File.expand_path( File.join( File.dirname( __FILE__ ), "../ext/fastthread" ) ) 5 | require 'fastthread' 6 | end 7 | 8 | class TestQueue < Test::Unit::TestCase 9 | def check_sequence( q ) 10 | range = "a".."f" 11 | 12 | s = "" 13 | e = nil 14 | 15 | t = Thread.new do 16 | begin 17 | for c in range 18 | q.push c 19 | s << c 20 | Thread.pass 21 | end 22 | rescue Exception => e 23 | end 24 | end 25 | 26 | for c in range 27 | unless t.alive? 28 | raise e if e 29 | assert_equal range.to_a.join, s, "expected all values pushed" 30 | end 31 | x = q.shift 32 | assert_equal c, x, "sequence error: expected #{ c } but got #{ x }" 33 | end 34 | end 35 | 36 | def test_queue 37 | check_sequence( Queue.new ) 38 | end 39 | 40 | def test_sized_queue_full 41 | check_sequence( SizedQueue.new( 6 ) ) 42 | end 43 | 44 | def test_sized_queue_half 45 | check_sequence( SizedQueue.new( 3 ) ) 46 | end 47 | 48 | def test_sized_queue_one 49 | check_sequence( SizedQueue.new( 1 ) ) 50 | end 51 | 52 | def check_serialization( k, *args ) 53 | q1 = k.new *args 54 | %w(a b c d e f).each { |c| q1.push c } 55 | q2 = Marshal.load(Marshal.dump(q1)) 56 | assert( ( q1.size == q2.size ), "queues are same size" ) 57 | q1.size.times do 58 | assert( ( q1.pop == q2.pop ), "same data" ) 59 | end 60 | [ q1, q2 ] 61 | end 62 | 63 | def test_queue_serialization 64 | check_serialization Queue 65 | end 66 | 67 | def test_sized_queue_serialization 68 | (q1, q2) = check_serialization SizedQueue, 20 69 | assert( ( q1.max == q2.max ), "maximum sizes equal" ) 70 | end 71 | 72 | def test_sized_queue_size 73 | q = SizedQueue.new 3 74 | assert_equal 3, q.max, "queue has expected max (3)" 75 | q.max = 5 76 | assert_equal 5, q.max, "queue has expected max (5)" 77 | end 78 | end 79 | 80 | -------------------------------------------------------------------------------- /projects/gem_plugin/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v0.3. Use Gem.path, not Gem.dir, so that local gem repositories work (rooster). 3 | 4 | v0.2.3. Signed gem. 5 | -------------------------------------------------------------------------------- /projects/gem_plugin/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/gem_plugin/Manifest: -------------------------------------------------------------------------------- 1 | test/test_plugins.rb 2 | setup.rb 3 | resources/resources/defaults.yaml 4 | resources/README 5 | resources/Rakefile 6 | resources/LICENSE 7 | resources/lib/project/init.rb 8 | resources/COPYING 9 | README 10 | Manifest 11 | LICENSE 12 | lib/gem_plugin.rb 13 | COPYING 14 | CHANGELOG 15 | bin/gpgen 16 | -------------------------------------------------------------------------------- /projects/gem_plugin/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("gem_plugin") do |p| 5 | p.author="Zed A. Shaw" 6 | p.project = "mongrel" 7 | p.summary = "A plugin system based on rubygems that uses dependencies only" 8 | 9 | p.need_tar_gz = false 10 | p.need_tgz = true 11 | p.require_signed = true 12 | p.certificate_chain = ['/Users/eweaver/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 13 | '/Users/eweaver/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 14 | 15 | p.test_pattern = "test/test_plugins.rb" 16 | p.clean_pattern += ["pkg", "lib/*.bundle", "*.gem", ".config"] 17 | p.rdoc_pattern = ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc'] 18 | p.rdoc_template = `allison --path`.chomp 19 | end 20 | 21 | namespace :site do 22 | task :rdoc => [:doc] do 23 | sh "rsync -azv --no-perms --no-times doc/* rubyforge.org:/var/www/gforge-projects/mongrel/gem_plugin_rdoc/" 24 | end 25 | end -------------------------------------------------------------------------------- /projects/gem_plugin/bin/gpgen: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | if ARGV.length != 1 6 | STDERR.puts "ERROR: You must give a name for your plugin and directory." 7 | STDERR.puts "usage: gpgen name" 8 | STDERR.puts "example: gpgen mygemplugin" 9 | exit 1 10 | end 11 | 12 | # setup the required binding variables for erb processing later 13 | project = ARGV.shift 14 | gem_plugin_base = File.expand_path(File.join(File.dirname(__FILE__), "..")) 15 | resources = File.join(gem_plugin_base, "resources") 16 | gem_plugin_version = gem_plugin_base[gem_plugin_base.rindex("-")+1 .. -1] 17 | 18 | 19 | # make the dir if it don't exist 20 | if not File.exist? project 21 | puts "Creating directory #{project}" 22 | mkdir project 23 | else 24 | puts "Directory #{project} exists, skipping." 25 | end 26 | 27 | 28 | # go through all the resource files, erb them, and write them verbatim to output 29 | # do not overwrite if exists already 30 | 31 | Dir.glob("#{resources}/**/*") do |infile| 32 | outfile = File.join(project, infile[resources.length .. -1]) 33 | if File.exist? outfile 34 | puts "File #{outfile} exists, skipping." 35 | else 36 | if File.directory? infile 37 | puts "Creating directory #{outfile}" 38 | mkdir_p outfile 39 | elsif File.file? infile 40 | puts "Creating file #{outfile}" 41 | open(infile) do |content| 42 | template = ERB.new(content.read) 43 | open(outfile,"w") {|f| f.write(template.result(binding)) } 44 | end 45 | else 46 | puts "!!! Resources contains something not a file or directory." 47 | puts "Skipping #{infile}." 48 | end 49 | end 50 | end 51 | 52 | # Finally, move the base init.rb to the right dir 53 | init_file = File.join(project,"lib",project) 54 | if File.exist? init_file 55 | puts "File init.rb already exists, skipping." 56 | puts "WARNING: There might be a junk '#{project}/lib/project/init.rb' file you can delete." 57 | else 58 | puts "Creating proper '#{project}/lib/#{project}/init.rb' file" 59 | mv File.join(project,"lib","project"), init_file 60 | end 61 | -------------------------------------------------------------------------------- /projects/gem_plugin/resources/COPYING: -------------------------------------------------------------------------------- 1 | No copying restrictions/license given. -------------------------------------------------------------------------------- /projects/gem_plugin/resources/LICENSE: -------------------------------------------------------------------------------- 1 | No license given. -------------------------------------------------------------------------------- /projects/gem_plugin/resources/README: -------------------------------------------------------------------------------- 1 | == <%= project.capitalize %> GemPlugin 2 | 3 | You should document your project here. 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/gem_plugin/resources/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/clean' 4 | require 'rake/gempackagetask' 5 | require 'rake/rdoctask' 6 | require 'fileutils' 7 | include FileUtils 8 | 9 | version="0.1" 10 | name="<%= project %>" 11 | 12 | spec = Gem::Specification.new do |s| 13 | s.name = name 14 | s.version = version 15 | s.description = s.summary = "The <%= project %> GemPlugin" 16 | s.author = "Nobody" 17 | s.add_dependency('gem_plugin', '>= <%= gem_plugin_version %>') 18 | 19 | s.platform = Gem::Platform::RUBY 20 | s.has_rdoc = true 21 | s.extra_rdoc_files = ["README"] 22 | 23 | s.files = %w(COPYING LICENSE README Rakefile) + 24 | Dir.glob("{bin,doc/rdoc,test,lib}/**/*") + 25 | Dir.glob("ext/**/*.{h,c,rb}") + 26 | Dir.glob("examples/**/*.rb") + 27 | Dir.glob("tools/*.rb") + 28 | Dir.glob("resources/**/*") 29 | 30 | s.require_path = "lib" 31 | s.bindir = "bin" 32 | end 33 | 34 | Rake::GemPackageTask.new(spec) do |p| 35 | p.need_tar = true if RUBY_PLATFORM !~ /mswin/ 36 | end 37 | 38 | task :install => [:test, :package] do 39 | sh %{sudo gem install pkg/#{name}-#{version}.gem} 40 | end 41 | 42 | task :uninstall => [:clean] do 43 | sh %{sudo gem uninstall #{name}} 44 | end 45 | 46 | Rake::TestTask.new do |t| 47 | t.libs << "test" 48 | t.test_files = FileList['test/test*.rb'] 49 | t.verbose = true 50 | end 51 | 52 | Rake::RDocTask.new do |rdoc| 53 | rdoc.rdoc_dir = 'doc/rdoc' 54 | rdoc.options << '--line-numbers' 55 | rdoc.rdoc_files.add ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc'] 56 | end 57 | 58 | task :default => [:test, :package] 59 | 60 | CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log', 'pkg', 'lib/*.bundle', '*.gem', '.config'] 61 | 62 | -------------------------------------------------------------------------------- /projects/gem_plugin/resources/lib/project/init.rb: -------------------------------------------------------------------------------- 1 | require 'gem_plugin' 2 | 3 | # give this class the name you want for your command <%= project %> 4 | class ChangeME < GemPlugin::Plugin "/somecategory" 5 | end 6 | 7 | -------------------------------------------------------------------------------- /projects/gem_plugin/resources/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | -------------------------------------------------------------------------------- /projects/gem_plugin/test/test_plugins.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'gem_plugin' 3 | 4 | include GemPlugin 5 | 6 | class ATestPlugin < GemPlugin::Plugin "/stuff" 7 | end 8 | 9 | class First < GemPlugin::Plugin "/commands" 10 | def initialize(options = {}) 11 | puts "First with options: #{options.inspect}" 12 | end 13 | end 14 | 15 | class Second < GemPlugin::Plugin "/commands" 16 | def initialize(options = {}) 17 | puts "Second with options: #{options.inspect}" 18 | end 19 | end 20 | 21 | class Last < GemPlugin::Plugin "/commands" 22 | def initialize(options = {}) 23 | puts "Last with options: #{options.inspect}" 24 | end 25 | end 26 | 27 | 28 | class PluginTest < Test::Unit::TestCase 29 | 30 | def setup 31 | @pmgr = Manager.instance 32 | @pmgr.load({"rails" => EXCLUDE}) 33 | @categories = ["/commands"] 34 | @names = ["/first", "/second", "/last", "/atestplugin"] 35 | end 36 | 37 | def test_load_plugins 38 | puts "#{@pmgr.plugins.inspect}" 39 | @pmgr.plugins.each {|cat,plugins| 40 | plugins.each do |n,p| 41 | puts "TEST: #{cat}#{n}" 42 | end 43 | } 44 | 45 | @pmgr.load 46 | @pmgr.plugins.each do |cat,plugins| 47 | plugins.each do |n,p| 48 | STDERR.puts "#{cat}#{n}" 49 | plugin = @pmgr.create("#{cat}#{n}", options={"name" => p}) 50 | end 51 | end 52 | end 53 | 54 | def test_similar_uris 55 | 56 | @pmgr.register("/test", "/testme", ATestPlugin) 57 | @pmgr.register("/test2", "/testme", ATestPlugin) 58 | 59 | assert_equal @pmgr.create("/test/testme").class, ATestPlugin 60 | assert_equal @pmgr.create("/test2/testme").class, ATestPlugin 61 | 62 | end 63 | 64 | 65 | def test_create 66 | last = @pmgr.create("/commands/last", "test" => "stuff") 67 | assert last != nil, "Didn't make the right plugin" 68 | first = @pmgr.create("/commands/last") 69 | assert first != nil, "Didn't make the right plugin" 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v1.0.5. Close #15406 (find_pid returning non-false) (Eden Li). 3 | 4 | v1.0.4. Actually ship the Cap 2 tasks; use an autorequire based on whether namespace() is available (Kevin Runde). 5 | 6 | v1.0.3. Signed gem. 7 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/Manifest: -------------------------------------------------------------------------------- 1 | bin/mongrel_cluster_ctl 2 | CHANGELOG 3 | COPYING 4 | lib/mongrel_cluster/init.rb 5 | lib/mongrel_cluster/recipes.rb 6 | lib/mongrel_cluster/recipes_1.rb 7 | lib/mongrel_cluster/recipes_2.rb 8 | LICENSE 9 | Manifest 10 | README 11 | resources/defaults.yaml 12 | resources/mongrel_cluster 13 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/README: -------------------------------------------------------------------------------- 1 | == Mongrel Cluster Plugin 2 | 3 | Tool to help start/stop/restart multiple mongrel servers to use behind a load balancer like Apache 2.2 (mod_proxy_balancer), Lighttpd, Pound, Pen or Balance. This plugin adds an option to specify a number of Mongrel servers to launch, a range of ports, and a configuration file for the cluster. Use "-h" to see command syntax. 4 | 5 | Configure cluster and write configuration file: 6 | mongrel_rails cluster::configure 7 | 8 | Start cluster: 9 | mongrel_rails cluster::start 10 | 11 | Restart cluster: 12 | mongrel_rails cluster::restart 13 | 14 | Stop cluster: 15 | mongrel_rails cluster::stop 16 | 17 | == Capistrano Recipes 18 | 19 | Add to config/deploy.rb: 20 | require 'mongrel_cluster/recipes' 21 | 22 | Variables: 23 | mongrel_servers: Number of Mongrel servers to start. 24 | mongrel_port: Starting port to bind to. 25 | mongrel_address: Address to bind to. 26 | mongrel_environment: Rails environment to run as. 27 | mongrel_conf: Path to conf file. Defaults to /etc/mongrel_cluster/app_name.conf 28 | mongrel_user: User to run mongrels in cluster as. Unset by default 29 | mongrel_group: Group to run mongrels in cluster as. Unset by default. 30 | 31 | On Capistrano 2 you get then get the following tasks: 32 | 33 | mongrel:cluster:configure Configure the cluster with variables. 34 | mongrel:cluster:start: Start Mongrel processes on the app server. 35 | mongrel:cluster:stop: Stop the Mongrel processes on the app server. 36 | mongrel:cluster:restart: Restart the Mongrel processes on the app server by starting and stopping mongrel_cluster. 37 | deploy:restart: Calls mongrel:cluster:restart to allow Mongrel to be used with the standard Capistrano deploy task. 38 | deploy:start: Calls mongrel:cluster:start to allow Mongrel to be used with the standard Capistrano deploy task. 39 | deploy:stop: Calls mongrel:cluster:stop to allow Mongrel to be used with the standard Capistrano deploy task. 40 | 41 | On Capistrano 1 you get the same tasks, but without the namespace: 42 | 43 | configure_mongrel_cluster: Configure the cluster with variables. 44 | start_mongrel_cluster: Start Mongrel processes on the app server. 45 | stop_mongrel_cluster: Stop the Mongrel processes on the app server. 46 | restart_mongrel_cluster: Restart the Mongrel processes on the app server by starting and stopping mongrel_cluster. 47 | restart: Calls restart_mongrel_cluster to allow Mongrel to be used with the standard Capistrano deploy task. 48 | spinner: Calls start_mongrel_cluster to allow Mongrel to be used with the standard Capistrano cold_deploy task. 49 | 50 | == Starting clusters at boot 51 | 52 | 1. Create mongrel_cluster conf directory (/etc/mongrel_cluster). 53 | 2. Assign ownership to your Capistrano user. 54 | 3. Copy the init.d script from this gem's resouces directory /etc/init.d. 55 | 4. chmod +x /etc/init.d/mongrel_cluster 56 | 5. Add to init.d startup. On RHEL/CentOS use: /sbin/chkconfig --level 345 mongrel_cluster on 57 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_cluster") do |p| 5 | p.summary = "Mongrel plugin that provides commands and Capistrano tasks for managing multiple Mongrel processes." 6 | p.project = "mongrel" 7 | p.author="Bradley Taylor" 8 | p.dependencies = ['gem_plugin >=0.2.3', 'mongrel >=1.0.2'] 9 | p.has_rdoc = false 10 | 11 | p.need_tar_gz = false 12 | p.need_tgz = true 13 | p.require_signed = true 14 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 15 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 16 | end 17 | 18 | # Is this still used? 19 | task :gem_source do 20 | mkdir_p "pkg/gems" 21 | 22 | FileList["**/*.gem"].each { |gem| mv gem, "pkg/gems" } 23 | FileList["pkg/*.tgz"].each {|tgz| rm tgz } 24 | rm_rf "pkg/#{name}-#{version}" 25 | 26 | sh %{ generate_yaml_index.rb -d pkg } 27 | sh %{ scp -r pkg/* #{ENV['SSH_USER']}@rubyforge.org:/var/www/gforge-projects/mongrel/releases/ } 28 | end 29 | 30 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/bin/mongrel_cluster_ctl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Copyright (c) 2006 Bradley Taylor, bradley@fluxura.com 3 | 4 | require 'optparse' 5 | 6 | def run(command, verbose, clean=false) 7 | Dir.chdir @options[:conf_path] do 8 | confs = Dir.glob("*.yml") 9 | confs += Dir.glob("*.conf") 10 | confs.each do |conf| 11 | cmd = "mongrel_rails cluster::#{command} -C #{conf}" 12 | cmd += " -v" if verbose 13 | cmd += " --clean" if clean 14 | puts cmd if verbose || command == "status" 15 | output = `#{cmd}` 16 | puts output if verbose || command == "status" 17 | puts "mongrel_rails cluster::#{command} returned an error." unless $?.success? 18 | end 19 | end 20 | end 21 | 22 | @options = {} 23 | @options[:conf_path] = "/etc/mongrel_cluster" 24 | @options[:verbose] = false 25 | @options[:clean] = false 26 | 27 | OptionParser.new do |opts| 28 | opts.banner = "Usage: #{$0} (start|stop|restart|status) [options]" 29 | 30 | opts.on("-c", "--conf_path PATH", "Path to mongrel_cluster configuration files") { |value| @options[:conf_path] = value } 31 | opts.on('-v', '--verbose', "Print all called commands and output.") { |value| @options[:verbose] = value } 32 | opts.on('--clean', "Remove pid files if needed beforehand.") { |value| @options[:clean] = value } 33 | 34 | if ARGV.empty? 35 | puts opts 36 | exit 37 | else 38 | @cmd = opts.parse!(ARGV) 39 | if @cmd.nil? 40 | puts opts 41 | exit 42 | end 43 | end 44 | 45 | end 46 | 47 | if @options[:conf_path] == nil && !File.directory?(@options[:conf_path]) 48 | puts "Invalid path to mongrel_cluster configuration files: #{@options[:conf_path]}" 49 | exit 50 | end 51 | 52 | case @cmd[0] 53 | when "start": 54 | puts "Starting all mongrel_clusters..." 55 | run "start", @options[:verbose], @options[:clean] 56 | when "stop": 57 | puts "Stopping all mongrel_clusters..." 58 | run "stop", @options[:verbose], @options[:clean] 59 | when "restart": 60 | puts "Restarting all mongrel_clusters..." 61 | run "stop", @options[:verbose], @options[:clean] 62 | run "start", @options[:verbose], @options[:clean] 63 | when "status": 64 | puts "Checking all mongrel_clusters..." 65 | run "status", @options[:verbose] 66 | else 67 | puts "Unknown command." 68 | end 69 | 70 | exit 71 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/lib/mongrel_cluster/recipes.rb: -------------------------------------------------------------------------------- 1 | 2 | if respond_to?(:namespace) 3 | require 'mongrel_cluster/recipes_2' # Cap 2 4 | else 5 | require 'mongrel_cluster/recipes_1' # Cap 1 6 | end 7 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/lib/mongrel_cluster/recipes_1.rb: -------------------------------------------------------------------------------- 1 | Capistrano.configuration(:must_exist).load do 2 | set :mongrel_servers, 2 3 | set :mongrel_port, 8000 4 | set :mongrel_address, "127.0.0.1" 5 | set :mongrel_environment, "production" 6 | set :mongrel_conf, nil 7 | set :mongrel_user, nil 8 | set :mongrel_group, nil 9 | set :mongrel_prefix, nil 10 | set :mongrel_rails, 'mongrel_rails' 11 | set :mongrel_clean, false 12 | set :mongrel_pid_file, nil 13 | set :mongrel_log_file, nil 14 | set :mongrel_config_script, nil 15 | 16 | desc <<-DESC 17 | Configure Mongrel processes on the app server. This uses the :use_sudo 18 | variable to determine whether to use sudo or not. By default, :use_sudo is 19 | set to true. 20 | DESC 21 | task :configure_mongrel_cluster, :roles => :app do 22 | set_mongrel_conf 23 | 24 | argv = [] 25 | argv << "#{mongrel_rails} cluster::configure" 26 | argv << "-N #{mongrel_servers.to_s}" 27 | argv << "-p #{mongrel_port.to_s}" 28 | argv << "-e #{mongrel_environment}" 29 | argv << "-a #{mongrel_address}" 30 | argv << "-c #{current_path}" 31 | argv << "-C #{mongrel_conf}" 32 | argv << "-P #{mongrel_pid_file}" if mongrel_pid_file 33 | argv << "-l #{mongrel_log_file}" if mongrel_log_file 34 | argv << "--user #{mongrel_user}" if mongrel_user 35 | argv << "--group #{mongrel_group}" if mongrel_group 36 | argv << "--prefix #{mongrel_prefix}" if mongrel_prefix 37 | argv << "-S #{mongrel_config_script}" if mongrel_config_script 38 | cmd = argv.join " " 39 | send(run_method, cmd) 40 | end 41 | 42 | desc <<-DESC 43 | Start Mongrel processes on the app server. This uses the :use_sudo variable to determine whether to use sudo or not. By default, :use_sudo is 44 | set to true. 45 | DESC 46 | task :start_mongrel_cluster , :roles => :app do 47 | set_mongrel_conf 48 | cmd = "#{mongrel_rails} cluster::start -C #{mongrel_conf}" 49 | cmd += " --clean" if mongrel_clean 50 | send(run_method, cmd) 51 | end 52 | 53 | desc <<-DESC 54 | Restart the Mongrel processes on the app server by starting and stopping the cluster. This uses the :use_sudo 55 | variable to determine whether to use sudo or not. By default, :use_sudo is set to true. 56 | DESC 57 | task :restart_mongrel_cluster , :roles => :app do 58 | set_mongrel_conf 59 | cmd = "#{mongrel_rails} cluster::restart -C #{mongrel_conf}" 60 | cmd += " --clean" if mongrel_clean 61 | send(run_method, cmd) 62 | end 63 | 64 | desc <<-DESC 65 | Stop the Mongrel processes on the app server. This uses the :use_sudo 66 | variable to determine whether to use sudo or not. By default, :use_sudo is 67 | set to true. 68 | DESC 69 | task :stop_mongrel_cluster , :roles => :app do 70 | set_mongrel_conf 71 | cmd = "#{mongrel_rails} cluster::stop -C #{mongrel_conf}" 72 | cmd += " --clean" if mongrel_clean 73 | send(run_method, cmd) 74 | end 75 | 76 | desc <<-DESC 77 | Check the status of the Mongrel processes on the app server. This uses the :use_sudo 78 | variable to determine whether to use sudo or not. By default, :use_sudo is 79 | set to true. 80 | DESC 81 | task :status_mongrel_cluster , :roles => :app do 82 | set_mongrel_conf 83 | send(run_method, "#{mongrel_rails} cluster::status -C #{mongrel_conf}") 84 | end 85 | 86 | desc <<-DESC 87 | Restart the Mongrel processes on the app server by calling restart_mongrel_cluster. 88 | DESC 89 | task :restart, :roles => :app do 90 | restart_mongrel_cluster 91 | end 92 | 93 | desc <<-DESC 94 | Start the Mongrel processes on the app server by calling start_mongrel_cluster. 95 | DESC 96 | task :spinner, :roles => :app do 97 | start_mongrel_cluster 98 | end 99 | 100 | def set_mongrel_conf 101 | set :mongrel_conf, "/etc/mongrel_cluster/#{application}.yml" unless mongrel_conf 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/lib/mongrel_cluster/recipes_2.rb: -------------------------------------------------------------------------------- 1 | Capistrano::Configuration.instance.load do 2 | set :mongrel_servers, 2 3 | set :mongrel_port, 8000 4 | set :mongrel_address, "127.0.0.1" 5 | set :mongrel_environment, "production" 6 | set :mongrel_conf, nil 7 | set :mongrel_user, nil 8 | set :mongrel_group, nil 9 | set :mongrel_prefix, nil 10 | set :mongrel_rails, "mongrel_rails" 11 | set :mongrel_clean, false 12 | set :mongrel_pid_file, nil 13 | set :mongrel_log_file, nil 14 | set :mongrel_config_script, nil 15 | 16 | namespace :mongrel do 17 | namespace :cluster do 18 | desc <<-DESC 19 | Configure Mongrel processes on the app server. This uses the :use_sudo 20 | variable to determine whether to use sudo or not. By default, :use_sudo is 21 | set to true. 22 | DESC 23 | task :configure, :roles => :app do 24 | set_conf 25 | 26 | argv = [] 27 | argv << "#{mongrel_rails} cluster::configure" 28 | argv << "-N #{mongrel_servers.to_s}" 29 | argv << "-p #{mongrel_port.to_s}" 30 | argv << "-e #{mongrel_environment}" 31 | argv << "-a #{mongrel_address}" 32 | argv << "-c #{current_path}" 33 | argv << "-C #{mongrel_conf}" 34 | argv << "-P #{mongrel_pid_file}" if mongrel_pid_file 35 | argv << "-l #{mongrel_log_file}" if mongrel_log_file 36 | argv << "--user #{mongrel_user}" if mongrel_user 37 | argv << "--group #{mongrel_group}" if mongrel_group 38 | argv << "--prefix #{mongrel_prefix}" if mongrel_prefix 39 | argv << "-S #{mongrel_config_script}" if mongrel_config_script 40 | argv << "--mongrel_rails #{mongrel_rails}" if mongrel_rails 41 | cmd = argv.join " " 42 | send(run_method, cmd) 43 | end 44 | 45 | desc <<-DESC 46 | Start Mongrel processes on the app server. This uses the :use_sudo variable to determine whether to use sudo or not. By default, :use_sudo is 47 | set to true. 48 | DESC 49 | task :start , :roles => :app do 50 | set_conf 51 | cmd = "#{mongrel_rails} cluster::start -C #{mongrel_conf}" 52 | cmd += " --clean" if mongrel_clean 53 | send(run_method, cmd) 54 | end 55 | 56 | desc <<-DESC 57 | Restart the Mongrel processes on the app server by starting and stopping the cluster. This uses the :use_sudo 58 | variable to determine whether to use sudo or not. By default, :use_sudo is set to true. 59 | DESC 60 | task :restart , :roles => :app do 61 | set_conf 62 | cmd = "#{mongrel_rails} cluster::restart -C #{mongrel_conf}" 63 | cmd += " --clean" if mongrel_clean 64 | send(run_method, cmd) 65 | end 66 | 67 | desc <<-DESC 68 | Stop the Mongrel processes on the app server. This uses the :use_sudo 69 | variable to determine whether to use sudo or not. By default, :use_sudo is 70 | set to true. 71 | DESC 72 | task :stop , :roles => :app do 73 | set_conf 74 | cmd = "#{mongrel_rails} cluster::stop -C #{mongrel_conf}" 75 | cmd += " --clean" if mongrel_clean 76 | send(run_method, cmd) 77 | end 78 | 79 | desc <<-DESC 80 | Check the status of the Mongrel processes on the app server. This uses the :use_sudo 81 | variable to determine whether to use sudo or not. By default, :use_sudo is 82 | set to true. 83 | DESC 84 | task :status , :roles => :app do 85 | set_conf 86 | send(run_method, "#{mongrel_rails} cluster::status -C #{mongrel_conf}") 87 | end 88 | 89 | def set_conf 90 | set :mongrel_conf, "/etc/mongrel_cluster/#{application}.yml" unless mongrel_conf 91 | end 92 | end 93 | end 94 | 95 | namespace :deploy do 96 | desc <<-DESC 97 | Restart the Mongrel processes on the app server by calling mongrel:cluster:restart. 98 | DESC 99 | task :restart, :roles => :app do 100 | mongrel.cluster.restart 101 | end 102 | 103 | desc <<-DESC 104 | Start the Mongrel processes on the app server by calling mongrel:cluster:start. 105 | DESC 106 | task :start, :roles => :app do 107 | mongrel.cluster.start 108 | end 109 | 110 | desc <<-DESC 111 | Stop the Mongrel processes on the app server by calling mongrel:cluster:stop. 112 | DESC 113 | task :stop, :roles => :app do 114 | mongrel.cluster.stop 115 | end 116 | end 117 | 118 | end 119 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | -------------------------------------------------------------------------------- /projects/mongrel_cluster/resources/mongrel_cluster: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2007 Bradley Taylor, bradley@railsmachine.com 4 | # 5 | # mongrel_cluster Startup script for Mongrel clusters. 6 | # 7 | # chkconfig: - 85 15 8 | # description: mongrel_cluster manages multiple Mongrel processes for use \ 9 | # behind a load balancer. 10 | # 11 | 12 | CONF_DIR=/etc/mongrel_cluster 13 | PID_DIR=/var/run/mongrel_cluster 14 | USER=mongrel 15 | 16 | RETVAL=0 17 | 18 | # Gracefully exit if the controller is missing. 19 | which mongrel_cluster_ctl >/dev/null || exit 0 20 | 21 | # Go no further if config directory is missing. 22 | [ -d "$CONF_DIR" ] || exit 0 23 | 24 | case "$1" in 25 | start) 26 | # Create pid directory 27 | mkdir -p $PID_DIR 28 | chown $USER:$USER $PID_DIR 29 | 30 | mongrel_cluster_ctl start -c $CONF_DIR 31 | RETVAL=$? 32 | ;; 33 | stop) 34 | mongrel_cluster_ctl stop -c $CONF_DIR 35 | RETVAL=$? 36 | ;; 37 | restart) 38 | mongrel_cluster_ctl restart -c $CONF_DIR 39 | RETVAL=$? 40 | ;; 41 | status) 42 | mongrel_cluster_ctl status -c $CONF_DIR 43 | RETVAL=$? 44 | ;; 45 | *) 46 | echo "Usage: mongrel_cluster {start|stop|restart|status}" 47 | exit 1 48 | ;; 49 | esac 50 | 51 | exit $RETVAL 52 | -------------------------------------------------------------------------------- /projects/mongrel_config/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v0.3.1. Signed gem. 3 | -------------------------------------------------------------------------------- /projects/mongrel_config/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_config/Manifest: -------------------------------------------------------------------------------- 1 | test/test_config.rb 2 | resources/style.css 3 | resources/index_win32.html 4 | resources/index.html 5 | resources/images/topbar.jpg 6 | resources/images/top.jpg 7 | resources/images/middle.jpg 8 | resources/images/bottom_orig.gif 9 | resources/images/bottom.gif 10 | resources/images/0171_new_page.png 11 | resources/images/0170_bubble.png 12 | resources/defaults.yaml 13 | README 14 | Manifest 15 | LICENSE 16 | lib/mongrel_config/win32_app.rb 17 | lib/mongrel_config/win32.rb 18 | lib/mongrel_config/init.rb 19 | lib/mongrel_config/app.rb 20 | COPYING 21 | CHANGELOG 22 | -------------------------------------------------------------------------------- /projects/mongrel_config/README: -------------------------------------------------------------------------------- 1 | = Mongrel Config Plugin 2 | 3 | This plugin uses the Camping micro-framework to implement a 4 | simple config tool for any Mongrel Rails application. 5 | Doesn't do too much right now but will be a littl more 6 | advanced later. 7 | 8 | -------------------------------------------------------------------------------- /projects/mongrel_config/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_config") do |p| 5 | p.summary = "Mongrel plugin that gives you web based config tool using Camping" 6 | p.project = "mongrel" 7 | p.test_pattern = "test/test_config.rb" 8 | p.author="Zed A. Shaw" 9 | p.dependencies = ['mongrel >=1.0.2', 'gem_plugin >=0.2.3', 'camping >=1.5'] 10 | 11 | p.need_tar_gz = false 12 | p.need_tgz = true 13 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 14 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 15 | p.require_signed = true 16 | end 17 | -------------------------------------------------------------------------------- /projects/mongrel_config/lib/mongrel_config/app.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'camping' 3 | require 'mongrel/camping' 4 | 5 | 6 | Camping.goes :Configure 7 | 8 | module Configure::Controllers 9 | class Index < R '/' 10 | def get 11 | render :show 12 | end 13 | end 14 | 15 | class Start < R '/start' 16 | def get 17 | render :start 18 | end 19 | 20 | def post 21 | @results = `mongrel_rails start -d -p #{input.port} -e #{input.env} -n #{input.num_procs} -a #{input.address}` 22 | render :start_done 23 | end 24 | end 25 | 26 | class Kill < R '/kill/(\w+)' 27 | 28 | def get(signal) 29 | if _running? 30 | @signal = signal.upcase 31 | pid = open($PID_FILE) {|f| f.read } 32 | begin 33 | Process.kill(@signal, pid.to_i) 34 | @results = "Mongrel sent PID #{pid} signal #{@signal}." 35 | rescue 36 | puts "ERROR: #$!" 37 | @results = "Failed to signal the Mongrel process. Maybe it is not running?

#$!

" 38 | end 39 | else 40 | @results = "Mongrel does not seem to be running. Maybe delete the pid file #{$PID_FILE} or start again." 41 | end 42 | 43 | render :kill 44 | end 45 | end 46 | 47 | 48 | class Stop < R '/stop' 49 | def get 50 | render :stop 51 | end 52 | end 53 | 54 | class Logs < R '/logs' 55 | def get 56 | @log_files = Dir.glob("log/**/*") 57 | render :logs 58 | end 59 | end 60 | 61 | end 62 | 63 | 64 | module Configure::Views 65 | def layout 66 | body_content = yield 67 | currently_running = _running? 68 | pid = _pid 69 | open(GemPlugin::Manager.instance.resource("mongrel_config", "/index.html")) do |f| 70 | template = ERB.new(f.read) 71 | template.result(binding) 72 | end 73 | end 74 | 75 | def show 76 | div do 77 | h2 { "Status" } 78 | if _running? 79 | p { "Currently running with PID #{_pid}." } 80 | else 81 | p { "Mongrel is not running." } 82 | end 83 | end 84 | end 85 | 86 | def start 87 | div do 88 | form :action => "/start", :method => "POST" do 89 | p { span { "Port:" }; input :name => "port", :value => "4000" } 90 | p { span { "Environment:" }; input :name => "env", :value => "development" } 91 | p { span { "Address:" }; input :name => "address", :value => "0.0.0.0" } 92 | input :type => "submit", :value => "START" 93 | end 94 | end 95 | end 96 | 97 | def start_done 98 | div do 99 | p { @results } 100 | end 101 | end 102 | 103 | def kill 104 | div do 105 | p { @results } 106 | 107 | case @signal 108 | when "HUP": 109 | p { "A reload (HUP) does not stop the process, but may not be complete." } 110 | when "TERM": 111 | p { "Stopped with TERM signal. The process should exit shortly, but only after processing pending requests." } 112 | when "USR2": 113 | p { "Complete restart (USR2) may take a little while. Check status in a few seconds or read logs." } 114 | when "KILL": 115 | p { "Process was violently stopped (KILL) so pending requests will be lost." } 116 | end 117 | end 118 | end 119 | 120 | def stop 121 | if _running? 122 | ul do 123 | li { a "Stop (TERM)", :href => "/kill/term" } 124 | li { a "Reload (HUP)", :href => "/kill/hup" } 125 | li { a "Restart (USR2)", :href => "/kill/usr2" } 126 | li { a "Kill (KILL)", :href => "/kill/kill" } 127 | end 128 | else 129 | p { "Mongrel does not appear to be running (no PID file at #$PID_FILE)." } 130 | end 131 | end 132 | 133 | def logs 134 | div do 135 | h2 { "Logs" } 136 | table do 137 | tr do 138 | th { "File"}; th { "Bytes" }; th { "Last Modified" } 139 | end 140 | @log_files.each do |file| 141 | tr do 142 | td { a file, :href => "../#{file}" } 143 | td { File.size file } 144 | td { File.mtime file } 145 | end 146 | end 147 | end 148 | end 149 | end 150 | 151 | def _running? 152 | File.exist? $PID_FILE 153 | end 154 | 155 | def _pid 156 | open($PID_FILE) {|f| f.read } if _running? 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /projects/mongrel_config/lib/mongrel_config/init.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'gem_plugin' 3 | require 'mongrel' 4 | 5 | 6 | class ConfigTool < GemPlugin::Plugin "/commands" 7 | include Mongrel::Command::Base 8 | 9 | def configure 10 | if RUBY_PLATFORM =~ /mswin/ 11 | options [ 12 | ['-h', '--host ADDR', "Host to bind to for server", :@host, "0.0.0.0"], 13 | ['-p', '--port NUMBER', "Port to bind to", :@port, "3001"], 14 | ['-u', '--uri URI', "Where to put your config tool", :@uri, "/config"], 15 | ['-R', '--mongrel PATH', "Path to mongrel_rails_service", :@mongrel_script, "c:\\ruby\\bin\\mongrel_rails_service"] 16 | ] 17 | else 18 | options [ 19 | ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd], 20 | ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"], 21 | ['-h', '--host ADDR', "Host to bind to for server", :@host, "0.0.0.0"], 22 | ['-p', '--port NUMBER', "Port to bind to", :@port, "3001"], 23 | ['-u', '--uri URI', "Where to put your config tool", :@uri, "/config"] 24 | ] 25 | end 26 | end 27 | 28 | def validate 29 | valid?(@uri, "Must give a uri") 30 | valid?(@port && @port.to_i > 0, "Must give a valid port") 31 | valid?(@host, "Host IP to bind must be given") 32 | 33 | if RUBY_PLATFORM !~ /mswin/ 34 | valid_dir? @cwd, "Cannot change to a directory that doesn't exist" 35 | Dir.chdir @cwd 36 | valid_dir? "log", "Log directory does not exist" 37 | end 38 | 39 | return @valid 40 | end 41 | 42 | 43 | def run 44 | # must require this here since rails and camping don't like eachother 45 | if RUBY_PLATFORM =~ /mswin/ 46 | require 'mongrel_config/win32_app' 47 | $mongrel_rails_service = @mongrel_script 48 | else 49 | require 'mongrel_config/app' 50 | end 51 | 52 | resources = GemPlugin::Manager.instance.resource "mongrel_config", "/" 53 | $PID_FILE = @pid_file 54 | 55 | $server = Mongrel::Camping::start(@host,@port,@uri,Configure) 56 | 57 | puts "** Configure is running at http://#{@host}:#{@port}#{@uri}" 58 | if RUBY_PLATFORM !~ /mswin/ 59 | trap("INT") { 60 | $server.stop 61 | } 62 | puts "Use CTRL-C to quit." 63 | else 64 | puts "Use CTRL-Pause/Break to quit." 65 | end 66 | 67 | # add our log directory 68 | $server.register("/log", Mongrel::DirHandler.new("log")) 69 | $server.register("/config/resources", Mongrel::DirHandler.new(resources)) 70 | 71 | $server.acceptor.join 72 | end 73 | end 74 | 75 | 76 | -------------------------------------------------------------------------------- /projects/mongrel_config/lib/mongrel_config/win32.rb: -------------------------------------------------------------------------------- 1 | require 'win32/service' 2 | 3 | 4 | # Simply abstracts the common stuff that the config tool needs to do 5 | # when dealing with Win32. It is a very thin wrapper which may expand 6 | # later. 7 | module W32Support 8 | 9 | # Lists all of the services that have "mongrel" in the binary_path_name 10 | # of the service. This detects the mongrel services. 11 | def W32Support.list 12 | Win32::Service.services.select {|s| s.binary_path_name =~ /mongrel/ } 13 | end 14 | 15 | # Just gets the display name of the service. 16 | def W32Support.display(name) 17 | Win32::Service.getdisplayname(name) 18 | end 19 | 20 | # Performs one operations (like :start or :start) which need 21 | # to be "monitored" until they're done. The wait_for parameter 22 | # should be a regex for the content of the status like /running/ 23 | # or /stopped/ 24 | def W32Support.do_and_wait(service_name, operation, wait_for) 25 | status = W32Support.status(service_name) 26 | if status =~ wait_for 27 | # already running call the block once and leave 28 | yield status 29 | else 30 | # start trying to start it 31 | Win32::Service.send(operation, service_name) 32 | status = W32Support.status(service_name) 33 | while status !~ wait_for 34 | yield status 35 | status = W32Support.status(service_name) 36 | end 37 | 38 | # do one last yield so they know it started 39 | yield status 40 | end 41 | end 42 | 43 | # Starts the requested service and calls a passed in block 44 | # until the service is done. You should sleep for a short 45 | # period until it's done or there's an exception. 46 | def W32Support.start(service_name) 47 | W32Support.do_and_wait(service_name, :start, /running/) do |status| 48 | yield status 49 | end 50 | end 51 | 52 | 53 | # Stops the service. Just like W32Support.start is will call 54 | # a block while it checks for the service to actually stop. 55 | def W32Support.stop(service_name) 56 | W32Support.do_and_wait(service_name, :stop, /stopped/) do |status| 57 | yield status 58 | end 59 | end 60 | 61 | 62 | # Returns the current_state field of the service. 63 | def W32Support.status(service_name) 64 | Win32::Service.status(service_name).current_state 65 | end 66 | 67 | 68 | # Deletes the service from the system. It first tries to stop 69 | # the service, and if you pass in a block it will call it while 70 | # the service is being stopped. 71 | def W32Support.delete(service_name) 72 | begin 73 | W32Support.stop(service_name) do |status| 74 | yield status if block_given? 75 | end 76 | rescue 77 | end 78 | 79 | begin 80 | Win32::Service.delete(service_name) 81 | rescue 82 | end 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /projects/mongrel_config/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/0170_bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/0170_bubble.png -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/0171_new_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/0171_new_page.png -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/bottom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/bottom.gif -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/bottom_orig.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/bottom_orig.gif -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/middle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/middle.jpg -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/top.jpg -------------------------------------------------------------------------------- /projects/mongrel_config/resources/images/topbar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_config/resources/images/topbar.jpg -------------------------------------------------------------------------------- /projects/mongrel_config/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mongrel Config Tool 5 | 6 | 7 | 8 | 9 |
10 |
11 |
Mongrel Config Tool
12 |
13 |
14 | 22 |
23 |
24 | <% if currently_running %> 25 | Status
26 |
27 | Mongrel is running with PID <%= pid %>. 28 | <% else %> 29 | Status
30 |
31 | Mongrel is not running. 32 | <% end %> 33 |

34 |
35 | Documentation
36 |
37 | Mongrel Home
38 |
39 | 40 | News
41 |
42 | 43 | Getting Started
44 |
45 | 46 | Win32 Guide
47 |
48 | 49 | Lighttpd Guide
50 |
51 | 52 | Mongrel API
53 |
54 | 55 | Ruby On Rails API
56 |
57 | 58 |

59 |
60 | 61 | <%= body_content %> 62 | 63 |
64 |
65 |
66 | 67 |
 
68 | 69 |
Copyright 2006 © Zed A. Shaw   |   Design by Kenneth Barbour.
70 | 71 | 72 | -------------------------------------------------------------------------------- /projects/mongrel_config/resources/index_win32.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mongrel Config Tool 5 | 6 | 7 | 8 | 9 |
10 |
11 |
Mongrel Config Tool
12 |
13 |
14 | 21 |
22 |
23 | <% if currently_running.length > 0 %> 24 | Running Services
25 |
26 | <%= currently_running.join(",") %> 27 | <% else %> 28 | No services running.
29 | <% end %> 30 |

31 |
32 | Documentation
33 |
34 | Mongrel Home
35 |
36 | 37 | News
38 |
39 | 40 | Getting Started
41 |
42 | 43 | Win32 Guide
44 |
45 | 46 | Lighttpd Guide
47 |
48 | 49 | Mongrel API
50 |
51 | 52 | Ruby On Rails API
53 |
54 | 55 |

56 |
57 | 58 | <%= body_content %> 59 | 60 |
61 |
62 |
63 | 64 |
 
65 | 66 |
Copyright 2006 © Zed A. Shaw   |   Design by Kenneth Barbour.
67 | 68 | 69 | -------------------------------------------------------------------------------- /projects/mongrel_config/resources/style.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | body { 3 | margin: 25px auto 0px auto; 4 | background-image: url(/config/resources/images/top.jpg); 5 | background-repeat: repeat-x; 6 | background-position: top; 7 | font-family: "Lucida Grande", Verdana, Halvetica, sans-serif; 8 | text-align: center; 9 | } 10 | a:hover { 11 | color: #990000; 12 | text-decoration: underline; 13 | } 14 | a { 15 | color: #990000; 16 | text-decoration: none; 17 | } 18 | #container { 19 | font-weight: normal; 20 | margin: 0px auto 0px auto; 21 | width: 686px; 22 | text-align: center; 23 | 24 | } 25 | #main { 26 | padding-top: 50px; 27 | padding-bottom: 50px; 28 | border: 1px solid #CC0000; 29 | height: auto; 30 | } 31 | #title { 32 | font-family: Georgia, "Times New Roman", Times, serif; 33 | font-size: 36px; 34 | color: #FFF; 35 | font-weight: normal; 36 | font-size:36px; 37 | text-align: center; 38 | color: #FFFFFF; 39 | padding-top: 28px; 40 | } 41 | #top { 42 | margin: 0px auto; 43 | width: 686px; 44 | height: 96px; 45 | background: url(/config/resources/images/topbar.jpg) no-repeat 46 | } 47 | #middle { 48 | background: url(/config/resources/images/middle.jpg) no-repeat; 49 | background-repeat: repeat-y; 50 | margin: -2px auto 0px auto; 51 | } 52 | #bottom { 53 | margin: -100px auto 0px; 54 | width: 686px; 55 | height: 106px; 56 | background: url(/config/resources/images/bottom.gif) no-repeat; 57 | clear: center; 58 | 59 | } 60 | #nav { 61 | width: 455px; 62 | padding: 20px 30px 10px; 63 | margin: 0px auto; 64 | color: #999; 65 | font-weight: normal; 66 | font-size: 11px; 67 | text-align: center; 68 | 69 | } 70 | #nav li { 71 | list-style: none; 72 | display: inline; 73 | margin-right: 20px; 74 | } 75 | #nav a { 76 | font-size: 10px; 77 | font-weight: bold; 78 | color: #999; 79 | text-decoration: none; 80 | } 81 | #nav a:hover { 82 | color: #990000; 83 | text-decoration: underline; 84 | } 85 | #content { 86 | padding: 0px 68px 1px 68px; 87 | margin: 0px auto 0px auto; 88 | text-align: left; 89 | height: 370px; 90 | } 91 | #side { 92 | margin: 30px auto 0px auto; 93 | width: 110px; 94 | border-left: 1px dotted #C4D0D7; 95 | padding: 0px 10px 0px 20px; 96 | color: #999; 97 | float: right; 98 | font-size: 70%; 99 | } 100 | #content h2 { 101 | color: #990000; 102 | font-weight: bolder; 103 | font-size: 14px; 104 | padding: 0px 135px 0px 10px; 105 | } 106 | #content h2 a:hover{ 107 | color: #990000; 108 | text-decoration: underline; 109 | } 110 | #content h2 a{ 111 | text-decoration: none; 112 | } 113 | #content h3 { 114 | color: #000000; 115 | font-size: 10px; 116 | letter-spacing: .3em; 117 | padding: 0px 135px 0px 10px; 118 | text-decoration: underline; 119 | 120 | } 121 | #content p { 122 | padding: 0px 170px 0px 10px; 123 | font-size: 70%; 124 | line-height: 1.7em; 125 | margin: 0px 0px 1.7em 0px; 126 | vertical-align: top; 127 | color: #666666; 128 | 129 | } 130 | #footertext { 131 | font-size: 10px; 132 | font-weight: bold; 133 | margin: 20px auto 0px; 134 | height: 20px; 135 | width: 686px; 136 | height: 31px; 137 | text-align: center; 138 | color: #888; 139 | } 140 | #footertext a { 141 | font-size: 10px; 142 | font-weight: bold; 143 | color: #999; 144 | text-decoration: none; 145 | } 146 | #footertext a:hover { 147 | text-decoration: underline; 148 | } 149 | 150 | b { font-size: 10pt; } 151 | 152 | #viewport { 153 | overflow: auto; width: 400px; height: 320px; 154 | } 155 | 156 | table { 157 | border-collapse: collapsed; 158 | } 159 | 160 | td { 161 | font-size: 11px; 162 | padding: 5px; 163 | } 164 | 165 | th { 166 | border: 1px #990000 solid; 167 | font-size: 12px; 168 | background-color: #955; 169 | color: #eeeeee; 170 | } -------------------------------------------------------------------------------- /projects/mongrel_config/test/test_config.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'mongrel_config/init' 3 | 4 | class CommandTest < Test::Unit::TestCase 5 | def setup 6 | @pmgr = GemPlugin::Manager.instance 7 | end 8 | 9 | def test_command_loaded 10 | assert @pmgr.create("/commands/configtool", "configtool not loaded") 11 | end 12 | 13 | end 14 | 15 | 16 | # these are only run if we're running on windows 17 | if RUBY_PLATFORM =~ /mswin/ 18 | require 'mongrel_config/win32' 19 | 20 | class Win32Test < Test::Unit::TestCase 21 | 22 | def test_list 23 | svcs = W32Support.list 24 | assert svcs.length > 0, "No services returned. Make sure you have one mongrel installed." 25 | end 26 | 27 | def test_display_name 28 | svcs = W32Support.list 29 | svcs.each { |s| W32Support.display(s.service_name) } 30 | end 31 | 32 | def test_start_stop 33 | svcs = W32Support.list 34 | svcs.each { |s| 35 | puts "Starting #{W32Support.display(s.service_name)} (might take a while)" 36 | i = 1 37 | W32Support.start(s.service_name) do |status| 38 | print "Starting #{s.service_name}: #{status} (#{i} seconds)\r" 39 | sleep 1 40 | i += 1 41 | end 42 | 43 | W32Support.stop(s.service_name) do |status| 44 | puts "Stopping #{s.service_name}: #{status}" 45 | sleep 1 46 | end 47 | } 48 | end 49 | 50 | end 51 | end 52 | 53 | 54 | -------------------------------------------------------------------------------- /projects/mongrel_console/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v0.2.1. Signed gem. 3 | -------------------------------------------------------------------------------- /projects/mongrel_console/COPYING: -------------------------------------------------------------------------------- 1 | No copying restrictions/license given. -------------------------------------------------------------------------------- /projects/mongrel_console/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_console/Manifest: -------------------------------------------------------------------------------- 1 | resources/defaults.yaml 2 | README 3 | Manifest 4 | LICENSE 5 | lib/mongrel_console/init.rb 6 | lib/mongrel_console/console.rb 7 | COPYING 8 | CHANGELOG 9 | -------------------------------------------------------------------------------- /projects/mongrel_console/README: -------------------------------------------------------------------------------- 1 | == Mongrel_console GemPlugin 2 | 3 | You should document your project here. 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/mongrel_console/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_console") do |p| 5 | p.summary = "Provides a combined Mongrel and Rails IRB console." 6 | p.project = "mongrel" 7 | p.author="Zed A. Shaw" 8 | p.dependencies = ['gem_plugin >=0.2.3', 'mongrel >=1.0.2'] 9 | 10 | p.need_tar_gz = false 11 | p.need_tgz = true 12 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 13 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 14 | p.require_signed = true 15 | end 16 | -------------------------------------------------------------------------------- /projects/mongrel_console/lib/mongrel_console/console.rb: -------------------------------------------------------------------------------- 1 | require "irb" 2 | begin 3 | require "irb/completion" 4 | rescue 5 | STDERR.puts "Problem lading irb/completion: #$!" 6 | end 7 | require 'rubygems' 8 | require 'yaml' 9 | require 'mongrel/rails' 10 | require 'config/environment' 11 | require 'dispatcher' 12 | require 'mongrel/debug' 13 | require 'net/http' 14 | 15 | class MongrelConsoleRunner 16 | 17 | def initialize 18 | @port = 3000 19 | @env = "development" 20 | end 21 | 22 | def tail(file="log/#{@env}.log") 23 | STDERR.puts "Tailing #{file}. CTRL-C to stop it." 24 | 25 | cursor = File.size(file) 26 | last_checked = Time.now 27 | tail_thread = Thread.new do 28 | File.open(file, 'r') do |f| 29 | loop do 30 | if f.mtime > last_checked 31 | f.seek cursor 32 | last_checked = f.mtime 33 | contents = f.read 34 | cursor += contents.length 35 | print contents 36 | end 37 | sleep 1 38 | end 39 | end 40 | end 41 | 42 | trap("INT") { tail_thread.kill } 43 | tail_thread.join 44 | nil 45 | end 46 | 47 | def start(port=@port, env=@env) 48 | `mongrel_rails start #{port} #{env} -d` 49 | end 50 | 51 | def stop 52 | `mongrel_rails stop` 53 | end 54 | 55 | def restart(port=@port, env=@env) 56 | stop 57 | start(port, env) 58 | end 59 | 60 | def status 61 | if File.exist? "log/mongrel.pid" 62 | pid = open("log/mongrel.pid") {|f| f.read.to_i } 63 | puts "Running on port #@port in env #@env with PID #{pid}" 64 | else 65 | puts "Mongrel not running." 66 | end 67 | end 68 | 69 | def get(url="/") 70 | Net::HTTP.get("localhost", url, @port) 71 | end 72 | end 73 | 74 | 75 | $mongrel = MongrelConsoleRunner.new 76 | puts "Starting console. mongrel.[start | stop | restart | status | tail | get]" 77 | $mongrel.status 78 | 79 | def self.mongrel 80 | $mongrel 81 | end 82 | 83 | IRB.start(__FILE__) 84 | -------------------------------------------------------------------------------- /projects/mongrel_console/lib/mongrel_console/init.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'gem_plugin' 3 | require 'mongrel' 4 | 5 | class Console < GemPlugin::Plugin "/commands" 6 | include Mongrel::Command::Base 7 | 8 | def configure 9 | options [ 10 | ['-c', '--chdir DIR', "Change to directory before running", :@dir, "."] 11 | ] 12 | end 13 | 14 | def validate 15 | valid_dir? @dir, "Directory is not valid" 16 | return @valid 17 | end 18 | 19 | def run 20 | begin 21 | Dir.chdir @dir 22 | load File.join(File.dirname(__FILE__), "console.rb") 23 | rescue Object 24 | STDERR.puts "Cannot run the console script: #$!" 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /projects/mongrel_console/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v1.1. First release. 3 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/Manifest: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | COPYING 3 | ext/uri_classifier/ext_help.h 4 | ext/uri_classifier/extconf.rb 5 | ext/uri_classifier/tst.h 6 | ext/uri_classifier/tst_cleanup.c 7 | ext/uri_classifier/tst_delete.c 8 | ext/uri_classifier/tst_grow_node_free_list.c 9 | ext/uri_classifier/tst_init.c 10 | ext/uri_classifier/tst_insert.c 11 | ext/uri_classifier/tst_search.c 12 | ext/uri_classifier/uri_classifier.c 13 | lib/mongrel_experimental.rb 14 | LICENSE 15 | Manifest 16 | README 17 | test/test_uriclassifier.rb 18 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/README: -------------------------------------------------------------------------------- 1 | = Mongrel Experimental 2 | 3 | 4 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_experimental") do |p| 5 | p.summary = "Backports and experimental features for Mongrel." 6 | p.project = "mongrel" 7 | p.author="The Mongrel Team" 8 | p.dependencies = ['mongrel =1.1'] 9 | 10 | p.need_tar_gz = false 11 | p.need_tgz = true 12 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 13 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 14 | p.require_signed = true 15 | end 16 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/ext_help.h: -------------------------------------------------------------------------------- 1 | #ifndef ext_help_h 2 | #define ext_help_h 3 | 4 | #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be."); 5 | #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name); 6 | #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T); 7 | 8 | #ifdef DEBUG 9 | #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__) 10 | #else 11 | #define TRACE() 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | dir_config("uri_classifier") 4 | have_library("c", "main") 5 | create_makefile("uri_classifier") 6 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | struct node 4 | { 5 | unsigned char value; 6 | struct node *left; 7 | struct node *middle; 8 | struct node *right; 9 | }; 10 | 11 | struct tst 12 | { 13 | int node_line_width; 14 | struct node_lines *node_lines; 15 | struct node *free_list; 16 | struct node *head[127]; 17 | }; 18 | 19 | struct node_lines 20 | { 21 | struct node *node_line; 22 | struct node_lines *next; 23 | }; 24 | 25 | enum tst_constants 26 | { 27 | TST_OK, TST_ERROR, TST_NULL_KEY, TST_DUPLICATE_KEY, TST_REPLACE, TST_LONGEST_MATCH 28 | }; 29 | 30 | struct tst *tst_init(int node_line_width); 31 | 32 | int tst_insert(unsigned char *key, void *data, struct tst *tst, int option, void **exist_ptr); 33 | 34 | void *tst_search(const unsigned char *key, struct tst *tst, int option, unsigned int *match_len); 35 | 36 | void *tst_delete(unsigned char *key, struct tst *tst); 37 | 38 | void tst_cleanup(struct tst *tst); 39 | 40 | 41 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst_cleanup.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tst.h" 3 | #include 4 | #include 5 | 6 | void tst_cleanup(struct tst *tst) 7 | { 8 | struct node_lines *current_line; 9 | struct node_lines *next_line; 10 | 11 | next_line = tst->node_lines; 12 | 13 | do 14 | { 15 | current_line = next_line; 16 | next_line = current_line->next; 17 | free(current_line->node_line); 18 | free(current_line); 19 | } 20 | while(next_line != NULL); 21 | 22 | free(tst); 23 | } 24 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst_delete.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tst.h" 3 | #include 4 | #include 5 | 6 | void *tst_delete(unsigned char *key, struct tst *tst) 7 | { 8 | struct node *current_node; 9 | struct node *current_node_parent; 10 | struct node *last_branch; 11 | struct node *last_branch_parent; 12 | struct node *next_node; 13 | struct node *last_branch_replacement; 14 | struct node *last_branch_dangling_child; 15 | int key_index; 16 | 17 | 18 | if(key[0] == 0) 19 | return NULL; 20 | if(tst->head[(int)key[0]] == NULL) 21 | return NULL; 22 | 23 | last_branch = NULL; 24 | last_branch_parent = NULL; 25 | current_node = tst->head[(int)key[0]]; 26 | current_node_parent = NULL; 27 | key_index = 1; 28 | while(current_node != NULL) 29 | { 30 | if(key[key_index] == current_node->value) 31 | { 32 | 33 | if( (current_node->left != NULL) || (current_node->right != NULL) ) 34 | { 35 | last_branch = current_node; 36 | last_branch_parent = current_node_parent; 37 | } 38 | if(key[key_index] == 0) 39 | break; 40 | else 41 | { 42 | current_node_parent = current_node; 43 | current_node = current_node->middle; 44 | key_index++; 45 | continue; 46 | } 47 | } 48 | else if( ((current_node->value == 0) && (key[key_index] < 64)) || 49 | ((current_node->value != 0) && (key[key_index] < 50 | current_node->value)) ) 51 | { 52 | last_branch_parent = current_node; 53 | current_node_parent = current_node; 54 | current_node = current_node->left; 55 | last_branch = current_node; 56 | continue; 57 | } 58 | else 59 | { 60 | last_branch_parent = current_node; 61 | current_node_parent = current_node; 62 | current_node = current_node->right; 63 | last_branch = current_node; 64 | continue; 65 | } 66 | 67 | } 68 | if(current_node == NULL) 69 | return NULL; 70 | 71 | if(last_branch == NULL) 72 | { 73 | 74 | next_node = tst->head[(int)key[0]]; 75 | tst->head[(int)key[0]] = NULL; 76 | } 77 | else if( (last_branch->left == NULL) && (last_branch->right == NULL) ) 78 | { 79 | 80 | if(last_branch_parent->left == last_branch) 81 | last_branch_parent->left = NULL; 82 | else 83 | last_branch_parent->right = NULL; 84 | 85 | next_node = last_branch; 86 | } 87 | else 88 | { 89 | 90 | if( (last_branch->left != NULL) && (last_branch->right != NULL) ) 91 | { 92 | last_branch_replacement = last_branch->right; 93 | last_branch_dangling_child = last_branch->left; 94 | } 95 | else if(last_branch->right != NULL) 96 | { 97 | last_branch_replacement = last_branch->right; 98 | last_branch_dangling_child = NULL; 99 | } 100 | else 101 | { 102 | last_branch_replacement = last_branch->left; 103 | last_branch_dangling_child = NULL; 104 | } 105 | 106 | if(last_branch_parent == NULL) 107 | tst->head[(int)key[0]]=last_branch_replacement; 108 | else 109 | { 110 | if (last_branch_parent->left == last_branch) 111 | last_branch_parent->left = last_branch_replacement; 112 | else if (last_branch_parent->right == last_branch) 113 | last_branch_parent->right = last_branch_replacement; 114 | else 115 | last_branch_parent->middle = last_branch_replacement; 116 | } 117 | 118 | if(last_branch_dangling_child != NULL) 119 | { 120 | current_node = last_branch_replacement; 121 | 122 | while (current_node->left != NULL) 123 | current_node = current_node->left; 124 | 125 | current_node->left = last_branch_dangling_child; 126 | } 127 | 128 | next_node = last_branch; 129 | } 130 | 131 | do 132 | { 133 | current_node = next_node; 134 | next_node = current_node->middle; 135 | 136 | current_node->left = NULL; 137 | current_node->right = NULL; 138 | current_node->middle = tst->free_list; 139 | tst->free_list = current_node; 140 | } 141 | while(current_node->value != 0); 142 | 143 | return next_node; 144 | 145 | } 146 | 147 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst_grow_node_free_list.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tst.h" 3 | #include 4 | #include 5 | 6 | int tst_grow_node_free_list(struct tst *tst) 7 | { 8 | struct node *current_node; 9 | struct node_lines *new_line; 10 | int i; 11 | 12 | 13 | if((new_line = (struct node_lines *) malloc(sizeof(struct node_lines))) == NULL) 14 | return TST_ERROR; 15 | 16 | if((new_line->node_line = (struct node *) 17 | calloc(tst->node_line_width, sizeof(struct node))) == NULL) 18 | { 19 | free(new_line); 20 | return TST_ERROR; 21 | } 22 | else 23 | { 24 | new_line->next = tst->node_lines; 25 | tst->node_lines = new_line; 26 | } 27 | 28 | current_node = tst->node_lines->node_line; 29 | tst->free_list = current_node; 30 | for (i = 1; i < tst->node_line_width; i++) 31 | { 32 | current_node->middle = &(tst->node_lines->node_line[i]); 33 | current_node = current_node->middle; 34 | } 35 | current_node->middle = NULL; 36 | return 1; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst_init.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tst.h" 3 | #include 4 | #include 5 | 6 | struct tst *tst_init(int width) 7 | { 8 | struct tst *tst; 9 | struct node *current_node; 10 | int i; 11 | 12 | 13 | if((tst = (struct tst *) calloc(1, sizeof(struct tst))) == NULL) 14 | return NULL; 15 | 16 | if ((tst->node_lines = (struct node_lines *) calloc(1, sizeof(struct node_lines))) == NULL) 17 | { 18 | free(tst); 19 | return NULL; 20 | } 21 | 22 | tst->node_line_width = width; 23 | tst->node_lines->next = NULL; 24 | if ((tst->node_lines->node_line = (struct node *) calloc(width, sizeof(struct node))) == NULL) 25 | { 26 | free(tst->node_lines); 27 | free(tst); 28 | return NULL; 29 | } 30 | 31 | current_node = tst->node_lines->node_line; 32 | tst->free_list = current_node; 33 | for (i = 1; i < width; i++) 34 | { 35 | current_node->middle = &(tst->node_lines->node_line[i]); 36 | current_node = current_node->middle; 37 | } 38 | current_node->middle = NULL; 39 | return tst; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/ext/uri_classifier/tst_search.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tst.h" 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | void *tst_search(const unsigned char *key, struct tst *tst, int option, 9 | unsigned int *match_len) 10 | { 11 | struct node *current_node; 12 | struct node *longest_match = NULL; 13 | unsigned int longest_match_len = 0; 14 | int key_index; 15 | 16 | assert(key != NULL && "key can't be NULL"); 17 | assert(tst != NULL && "tst can't be NULL"); 18 | 19 | if (key[0] == 0) 20 | return NULL; 21 | 22 | if (tst->head[(int) key[0]] == NULL) 23 | return NULL; 24 | 25 | if (match_len) 26 | *match_len = 0; 27 | 28 | current_node = tst->head[(int) key[0]]; 29 | key_index = 1; 30 | 31 | while (current_node != NULL) { 32 | if (key[key_index] == current_node->value) { 33 | if (current_node->value == 0) { 34 | if (match_len) 35 | *match_len = key_index; 36 | return current_node->middle; 37 | } else { 38 | current_node = current_node->middle; 39 | key_index++; 40 | continue; 41 | } 42 | } else { 43 | if (current_node->value == 0) { 44 | if (option & TST_LONGEST_MATCH) { 45 | longest_match = current_node->middle; 46 | longest_match_len = key_index; 47 | } 48 | 49 | if (key[key_index] < 64) { 50 | current_node = current_node->left; 51 | continue; 52 | } else { 53 | current_node = current_node->right; 54 | continue; 55 | } 56 | } else { 57 | if (key[key_index] < current_node->value) { 58 | current_node = current_node->left; 59 | continue; 60 | } else { 61 | current_node = current_node->right; 62 | continue; 63 | } 64 | } 65 | } 66 | } 67 | 68 | if (match_len) 69 | *match_len = longest_match_len; 70 | 71 | return longest_match; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /projects/mongrel_experimental/lib/mongrel_experimental.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'uri_classifier' 3 | STDERR.puts "** Mongrel_experimental loaded" 4 | -------------------------------------------------------------------------------- /projects/mongrel_service/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | * SVN * 3 | 4 | * 0.3.3 * 5 | 6 | * Properly display package/gem version for mongrel_service. Closes #13823. 7 | 8 | * Updated ServiceFB to r80 to solve issue when compiling with FB > 0.17. 9 | 10 | * 0.3.2 * 11 | 12 | * Solved detection of parent process at ServiceFB level 13 | (solves the x64 Windows issues). 14 | 15 | * Upgraded to ServiceFB 'trunk' (but pistoned it, just in case). 16 | 17 | * Fixed problems with ruby installations outside PATH or inside folders with spaces. 18 | 19 | * Activate FB pedantic warnings by default (is really useful). 20 | 21 | * 0.3.1 * 22 | 23 | * Single Service (SingleMongrel) object type implemented. 24 | 25 | * Updated Rakefile to reflect the new building steps. 26 | 27 | * Removed SendSignal, too hackish for my taste, replaced with complete FB solution. 28 | 29 | * Added basic Process monitoring and re-spawning. 30 | -------------------------------------------------------------------------------- /projects/mongrel_service/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Luis Lavena, luislavena@gmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /projects/mongrel_service/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_service/Manifest: -------------------------------------------------------------------------------- 1 | bin/mongrel_service.exe 2 | tools/freebasic.rb 3 | TODO 4 | resources/defaults.yaml 5 | README 6 | native/mongrel_service.bi 7 | native/mongrel_service.bas 8 | native/console_process.bi 9 | native/console_process.bas 10 | native/_debug.bi 11 | LICENSE 12 | lib/ServiceFB/ServiceFB_Utils.bi 13 | lib/ServiceFB/ServiceFB_Utils.bas 14 | lib/ServiceFB/ServiceFB.bi 15 | lib/ServiceFB/ServiceFB.bas 16 | lib/ServiceFB/_utils_internals.bi 17 | lib/ServiceFB/_internals.bi 18 | lib/mongrel_service/init.rb 19 | COPYING 20 | CHANGELOG 21 | Manifest 22 | -------------------------------------------------------------------------------- /projects/mongrel_service/README: -------------------------------------------------------------------------------- 1 | == Mongrel Native Win32 Service Plugin 2 | 3 | This plugin offer native win32 services for rails. This replace mongrel_rails_service. 4 | It will work like before, with this this syntax when calling mongrel_rails: 5 | 6 | service::install 7 | service::remove 8 | service::update 9 | 10 | = Author: 11 | Luis Lavena 12 | -------------------------------------------------------------------------------- /projects/mongrel_service/Rakefile: -------------------------------------------------------------------------------- 1 | require 'echoe' 2 | require 'tools/freebasic' 3 | 4 | # Task :package needs compile before doing the gem stuff. 5 | # (weird behavior of Rake?) 6 | task :package => [:compile] 7 | 8 | echoe_spec = Echoe.new("mongrel_service") do |p| 9 | p.summary = "Mongrel Native Win32 Service Plugin for Rails" 10 | p.summary += " (debug build)" unless ENV['RELEASE'] 11 | p.description = "This plugin offer native win32 services for rails, powered by Mongrel." 12 | p.author = "Luis Lavena" 13 | p.platform = Gem::Platform::WIN32 14 | p.dependencies = ['gem_plugin >=0.2.3', 'mongrel >=1.0.2', 'win32-service >=0.5.0'] 15 | 16 | p.executable_pattern = "" 17 | 18 | p.need_tar_gz = false 19 | p.need_zip = true 20 | p.certificate_chain = ['~/gem_certificates/mongrel-public_cert.pem', 21 | '~/gem_certificates/luislavena-mongrel-public_cert.pem'] 22 | p.require_signed = true 23 | end 24 | 25 | desc "Compile native code" 26 | task :compile => [:native_lib, :native_service] 27 | 28 | # global options shared by all the project in this Rakefile 29 | OPTIONS = { 30 | :debug => false, 31 | :profile => false, 32 | :errorchecking => :ex, 33 | :mt => true, 34 | :pedantic => true } 35 | 36 | OPTIONS[:debug] = true if ENV['DEBUG'] 37 | OPTIONS[:profile] = true if ENV['PROFILE'] 38 | OPTIONS[:errorchecking] = :exx if ENV['EXX'] 39 | OPTIONS[:pedantic] = false if ENV['NOPEDANTIC'] 40 | 41 | # ServiceFB namespace (lib) 42 | namespace :lib do 43 | project_task 'servicefb' do 44 | lib 'ServiceFB' 45 | build_to 'lib' 46 | 47 | define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE'] 48 | source 'lib/ServiceFB/ServiceFB.bas' 49 | 50 | option OPTIONS 51 | end 52 | 53 | project_task 'servicefb_utils' do 54 | lib 'ServiceFB_Utils' 55 | build_to 'lib' 56 | 57 | define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE'] 58 | source 'lib/ServiceFB/ServiceFB_Utils.bas' 59 | 60 | option OPTIONS 61 | end 62 | end 63 | 64 | # add lib namespace to global tasks 65 | #include_projects_of :lib 66 | task :native_lib => "lib:build" 67 | task :clean => "lib:clobber" 68 | 69 | # mongrel_service (native) 70 | namespace :native do 71 | project_task 'mongrel_service' do 72 | executable 'mongrel_service' 73 | build_to 'bin' 74 | 75 | define 'DEBUG_LOG' unless ENV['RELEASE'] 76 | define "GEM_VERSION=#{echoe_spec.version}" 77 | 78 | main 'native/mongrel_service.bas' 79 | source 'native/console_process.bas' 80 | 81 | lib_path 'lib' 82 | library 'ServiceFB', 'ServiceFB_Utils' 83 | library 'user32', 'advapi32', 'psapi' 84 | 85 | option OPTIONS 86 | end 87 | end 88 | 89 | #include_projects_of :native 90 | task :native_service => "native:build" 91 | task :clean => "native:clobber" 92 | -------------------------------------------------------------------------------- /projects/mongrel_service/TODO: -------------------------------------------------------------------------------- 1 | Legend: 2 | [ ] not done 3 | [X] done 4 | [+] in progess 5 | 6 | ### General 7 | [ ] Add more documentation about services and requirements 8 | [ ] Add process monitoring. 9 | 10 | ### Dependencies 11 | [+] Remove win32/service extension dependency (instead of relying in the non-official 0.5.0 one). 12 | [ ] Add service management (from ServiceFB) to install and remove services. 13 | 14 | ### SingleMongrel 15 | [+] Sanitize SingleMongrel and document the functions. 16 | 17 | ### ClusterMongrel 18 | [ ] Parse mongrel_cluster configuration file (yaml) looking for port information 19 | [ ] Reimplent SingleMongrel for ClusterMongrel, splitting source files for better organization. 20 | -------------------------------------------------------------------------------- /projects/mongrel_service/lib/ServiceFB/ServiceFB.bas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evan/mongrel/5d6965e19ee9d555dec6c283ad9ce0199bca36e3/projects/mongrel_service/lib/ServiceFB/ServiceFB.bas -------------------------------------------------------------------------------- /projects/mongrel_service/lib/ServiceFB/ServiceFB.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | #if __FB_VERSION__ < "0.17" 9 | #error ServiceFB is designed to compile with FreeBASIC version "0.17" 10 | #else 11 | 12 | #ifndef __FB_WIN32__ 13 | #error Platform unsupported. Compiling ServiceFB requires Windows platform. 14 | #else 15 | 16 | #ifndef __ServiceFB_bi__ 17 | #define __ServiceFB_bi__ 18 | 19 | #include once "windows.bi" 20 | #inclib "advapi32" 21 | 22 | namespace fb 23 | namespace svc '# fb.svc 24 | #ifdef SERVICEFB_DEBUG_LOG 25 | '# debug print 26 | declare sub _dprint(byref as string) 27 | #else 28 | #define _dprint(message) 29 | #endif 30 | 31 | '# service states used by end user with 'state' property 32 | enum ServiceStateEnum 33 | Running = SERVICE_RUNNING 34 | Paused = SERVICE_PAUSED 35 | Stopped = SERVICE_STOPPED 36 | end enum 37 | 38 | 39 | '# ServiceProcess type (object) 40 | '# use this to create new services and reference the on*() methods to perform the related 41 | '# tasks. 42 | type ServiceProcess 43 | '# ctor/dtor 44 | declare constructor() 45 | declare constructor(byref as string) 46 | declare destructor() 47 | 48 | '# methods (public) 49 | declare sub Run() 50 | declare sub StillAlive(byval as integer = 10) 51 | 52 | '# helper methods (private) 53 | declare sub UpdateState(byval as DWORD, byval as integer = 0, byval as integer = 0) 54 | 55 | '# pseudo-events 56 | '# for onInit you should return FALSE (0) in case you want to abort 57 | '# service initialization. 58 | '# If everything was ok, then return TRUE (-1) 59 | onInit as function(byref as ServiceProcess) as integer 60 | onStart as sub(byref as ServiceProcess) 61 | onStop as sub(byref as ServiceProcess) 62 | onPause as sub(byref as ServiceProcess) 63 | onContinue as sub(byref as ServiceProcess) 64 | 65 | '# call_* are used to avoid the warning arround ThreadCreate 66 | declare static sub call_onStart(byval as any ptr) 67 | 68 | '# properties (public) 69 | name as string 70 | description as string 71 | state as ServiceStateEnum 72 | commandline as string '# TODO 73 | shared_process as integer 74 | 75 | '# properties (private) 76 | _svcStatus as SERVICE_STATUS 77 | _svcHandle as SERVICE_STATUS_HANDLE 78 | _svcStopEvent as HANDLE 79 | _threadHandle as any ptr 80 | end type 81 | 82 | 83 | '# ServiceHost type (object) 84 | '# use this, beside ServiceProcess, to manage the registration and running of 85 | '# several services sharing the same process. 86 | '# NOTE: ServiceHost.Run() and ServiceProcess.Run() are mutually exclusive, that 87 | '# means don't mix single service with multiple service in the same program! 88 | type ServiceHost 89 | '# ctor/dtor() 90 | declare constructor() 91 | declare destructor() 92 | 93 | '# methods (public) 94 | declare sub Add(byref as ServiceProcess) 95 | declare sub Run() 96 | 97 | '# properties (public) 98 | count as integer 99 | end type 100 | end namespace '# fb.svc 101 | end namespace '# fb 102 | 103 | #ifdef SERVICEFB_INCLUDE_UTILS 104 | #include once "ServiceFB_Utils.bi" 105 | #endif 106 | 107 | #endif '# __ServiceFB_bi__ 108 | #endif '# __FB_WIN32__ 109 | #endif '# __FB_VERSION__ -------------------------------------------------------------------------------- /projects/mongrel_service/lib/ServiceFB/ServiceFB_Utils.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | #if __FB_VERSION__ < "0.17" 9 | #error ServiceFB is designed to compile with FreeBASIC version "0.17" 10 | #else 11 | 12 | #ifndef __FB_WIN32__ 13 | #error Platform unsupported. Compiling ServiceFB requires Windows platform. 14 | #else 15 | 16 | #ifndef __ServiceFB_Utils_bi__ 17 | #define __ServiceFB_Utils_bi__ 18 | 19 | #include once "win/psapi.bi" 20 | #include once "win/tlhelp32.bi" 21 | 22 | namespace fb 23 | namespace svc 24 | namespace utils '# fb.svc.utils 25 | '# use this to determine (using select case maybe?) the 26 | '# mode which the service was invoked. 27 | enum ServiceRunMode 28 | RunAsUnknown = 0 29 | RunAsService 30 | RunAsManager 31 | RunAsConsole 32 | end enum 33 | 34 | 35 | '# ServiceController type (object) 36 | '# this is a helper object in case you want to implement 37 | '# console mode (command line testing/debugging) and management (install/remove/control) 38 | '# to your services, all from the same executable 39 | type ServiceController 40 | '# ctor/dtor() 41 | declare constructor() 42 | declare constructor(byref as string) 43 | declare constructor(byref as string, byref as string) 44 | declare constructor(byref as string, byref as string, byref as string) 45 | declare destructor() 46 | 47 | '# methods (public) 48 | declare sub Banner() 49 | declare function RunMode() as ServiceRunMode 50 | declare sub Manage() 51 | declare sub Manage(byref as ServiceProcess) 52 | declare sub Console() 53 | declare sub Console(byref as ServiceProcess) 54 | 55 | '# properties (public) 56 | '# use these properties for shwoing information on console/manager mode 57 | '# as banner. 58 | '# Product, version 59 | '# copyright 60 | product as string 61 | version as string 62 | copyright as string 63 | end type 64 | end namespace '# fb.svc.utils 65 | end namespace '# fb.svc 66 | end namespace '# fb 67 | 68 | #endif '# __ServiceFB_bi__ 69 | #endif '# __FB_WIN32__ 70 | #endif '# __FB_VERSION__ -------------------------------------------------------------------------------- /projects/mongrel_service/lib/ServiceFB/_internals.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | '################################################################## 9 | '# 10 | '# DO NOT INCLUDE THIS FILE DIRECTLY! 11 | '# it is used internaly by ServiceFB 12 | '# use ServiceFB.bi instead 13 | '# 14 | '################################################################## 15 | 16 | namespace fb 17 | namespace svc 18 | '# now due references locking, I needed a constructor and destructor for 19 | '# the namespace to garantee everything is cleaned up on termination of the process 20 | declare sub _initialize() constructor 21 | declare sub _terminate() destructor 22 | 23 | '# global service procedures (private) 24 | declare sub _main(byval as DWORD, byval as LPSTR ptr) 25 | declare function _control_ex(byval as DWORD, byval as DWORD, byval as LPVOID, byval as LPVOID) as DWORD 26 | declare sub _run() 27 | 28 | '# global references helper 29 | declare function _add_to_references(byref as ServiceProcess) as integer 30 | declare function _find_in_references(byref as string) as ServiceProcess ptr 31 | 32 | '# command line builder (helper) 33 | '# this is used to gather information about: 34 | '# mode (if present) 35 | '# valid service name (after lookup in the table) 36 | '# command line to be passed to service 37 | declare sub _build_commandline(byref as string, byref as string, byref as string) 38 | 39 | '# I started this as simple, unique service served from one process 40 | '# but the idea of share the same process space (and reduce resources use) was good. 41 | '# to do that, I needed a references table (similar to service_table, but we will 42 | '# hold the ServiceProcess registered by ServiceHost (the multi services host). 43 | '# also, I needed a locking mechanism to avoid problems of two calls changing the table 44 | '# at the same time. 45 | extern _svc_references as ServiceProcess ptr ptr 46 | extern _svc_references_count as integer 47 | extern _svc_references_lock as any ptr 48 | end namespace '# fb.svc 49 | end namespace '# fb 50 | 51 | -------------------------------------------------------------------------------- /projects/mongrel_service/lib/ServiceFB/_utils_internals.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | '################################################################## 9 | '# 10 | '# DO NOT INCLUDE THIS FILE DIRECTLY! 11 | '# it is used internaly by ServiceFB 12 | '# use ServiceFB_Utils.bi instead 13 | '# 14 | '################################################################## 15 | 16 | namespace fb 17 | namespace svc 18 | namespace utils '# fb.svc.utils 19 | '# console_handler is used to get feedback form keyboard and allow 20 | '# shutdown of service using Ctrl+C / Ctrl+Break from keyboard 21 | declare function _console_handler(byval as DWORD) as BOOL 22 | 23 | '# helper private subs used to list the services and their descriptions 24 | '# in _svc_references 25 | declare sub _list_references() 26 | 27 | '# internals functions used to get Parent PID and Process Name 28 | '# using this we automatically determine if the service was started by SCM 29 | '# or by the user, from commandline or from explorer 30 | declare function _parent_pid(byval as uinteger) as uinteger 31 | declare function _process_name(byval as uinteger) as string 32 | declare function _process_name_dyn_psapi(byval as uinteger) as string 33 | declare function _show_error() as string 34 | 35 | '# InStrRev (authored by ikkejw @ freebasic forums) 36 | '# http://www.freebasic.net/forum/viewtopic.php?p=49315#49315 37 | declare function InStrRev(byval as uinteger = 0, byref as string, byref as string) as uinteger 38 | 39 | '# use a signal (condition) in the console mode to know 40 | '# when the service should be stopped. 41 | '# the Console() main loop will wait for it, and the console_handler 42 | '# will raise in case Ctrl+C / Ctrl+Break or other events are 43 | '# received. 44 | extern _svc_stop_signal as any ptr 45 | extern _svc_in_console as ServiceProcess ptr 46 | extern _svc_in_console_stop_flag as BOOL 47 | end namespace '# fb.svc.utils 48 | end namespace '# fb.svc 49 | end namespace '# fb 50 | -------------------------------------------------------------------------------- /projects/mongrel_service/native/_debug.bi: -------------------------------------------------------------------------------- 1 | '################################################################## 2 | '# 3 | '# mongrel_service: Win32 native implementation for mongrel 4 | '# (using ServiceFB and FreeBASIC) 5 | '# 6 | '# Copyright (c) 2006 Multimedia systems 7 | '# (c) and code by Luis Lavena 8 | '# 9 | '# mongrel_service (native) and mongrel_service gem_pluing are licensed 10 | '# in the same terms as mongrel, please review the mongrel license at 11 | '# http://mongrel.rubyforge.org/license.html 12 | '# 13 | '################################################################## 14 | 15 | '################################################################## 16 | '# Requirements: 17 | '# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006). 18 | '# 19 | '################################################################## 20 | 21 | #ifndef __Debug_bi__ 22 | #define __Debug_bi__ 23 | 24 | #ifdef DEBUG_LOG 25 | #include once "vbcompat.bi" 26 | #ifndef DEBUG_LOG_FILE 27 | #define DEBUG_LOG_FILE EXEPATH + "\debug.log" 28 | #endif 29 | 30 | '# this procedure is only used for debugging purposed, will be removed from 31 | '# final compilation 32 | private sub debug_to_file(byref message as string, byref file as string, byval linenumber as uinteger, byref func as string) 33 | dim handle as integer 34 | static first_time as integer 35 | 36 | handle = freefile 37 | open DEBUG_LOG_FILE for append as #handle 38 | 39 | if (first_time = 0) then 40 | print #handle, "# Logfile created on "; format(now(), "dd/mm/yyyy HH:mm:ss") 41 | print #handle, "" 42 | first_time = 1 43 | end if 44 | 45 | '# src/module.bas:123, namespace.function: 46 | '# message 47 | '# 48 | print #handle, file; ":"; str(linenumber); ", "; lcase(func); ":" 49 | print #handle, space(2); message 50 | print #handle, "" 51 | 52 | close #handle 53 | end sub 54 | #define debug(message) debug_to_file(message, __FILE__, __LINE__, __FUNCTION__) 55 | #else 56 | #define debug(message) 57 | #endif '# DEBUG_LOG 58 | 59 | #endif '# __Debug_bi__ 60 | -------------------------------------------------------------------------------- /projects/mongrel_service/native/boolean.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | #ifndef __BOOLEAN_BI__ 9 | #define __BOOLEAN_BI__ 10 | 11 | #undef BOOLEAN 12 | type BOOLEAN as byte 13 | #undef FALSE 14 | const FALSE as byte = 0 15 | #undef TRUE 16 | const TRUE as byte = not FALSE 17 | 18 | #endif ' __BOOLEAN_BI__ -------------------------------------------------------------------------------- /projects/mongrel_service/native/console_process.bi: -------------------------------------------------------------------------------- 1 | '#-- 2 | '# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems 3 | '# 4 | '# This source code is released under the MIT License. 5 | '# See MIT-LICENSE file for details 6 | '#++ 7 | 8 | #ifndef __CONSOLE_PROCESS_BI__ 9 | #define __CONSOLE_PROCESS_BI__ 10 | 11 | #include once "windows.bi" 12 | #include once "boolean.bi" 13 | 14 | enum ProcessStdEnum 15 | ProcessStdOut = 1 16 | ProcessStdErr = 2 17 | ProcessStdBoth = 3 18 | end enum 19 | 20 | type ConsoleProcess 21 | '# this class provide basic functionality 22 | '# to control child processes 23 | 24 | '# new ConsoleProcess(Filename, Parameters) 25 | declare constructor(byref as string = "", byref as string = "") 26 | 27 | '# delete 28 | declare destructor() 29 | 30 | '# properties (only getters) 31 | declare property filename as string 32 | declare property filename(byref as string) 33 | 34 | declare property arguments as string 35 | declare property arguments(byref as string) 36 | 37 | '# stdout and stderr allow you redirect 38 | '# console output and errors to files 39 | declare property redirected_stdout as string 40 | declare property redirected_stderr as string 41 | 42 | '# evaluate if the process is running 43 | declare property running as boolean 44 | 45 | '# pid will return the current Process ID, or 0 if no process is running 46 | declare property pid as uinteger 47 | 48 | '# exit_code is the value set by the process prior exiting. 49 | declare property exit_code as uinteger 50 | 51 | '# methods 52 | declare function redirect(byval as ProcessStdEnum, byref as string) as boolean 53 | declare function start() as boolean 54 | declare function terminate(byval as boolean = false) as boolean 55 | 56 | private: 57 | _filename as string 58 | _arguments as string 59 | _pid as uinteger 60 | _process_info as PROCESS_INFORMATION 61 | _show_console as boolean = false 62 | 63 | _redirect_stdout as boolean 64 | _stdout_filename as string 65 | 66 | _redirect_stderr as boolean 67 | _stderr_filename as string 68 | 69 | '# this fake console handler 70 | '# is used to trap ctrl-c 71 | declare static function _console_handler(byval as DWORD) as BOOL 72 | 73 | end type 'ConsoleProcess 74 | 75 | #endif '__CONSOLE_PROCESS_BI__ 76 | -------------------------------------------------------------------------------- /projects/mongrel_service/native/mongrel_service.bi: -------------------------------------------------------------------------------- 1 | '################################################################## 2 | '# 3 | '# mongrel_service: Win32 native implementation for mongrel 4 | '# (using ServiceFB and FreeBASIC) 5 | '# 6 | '# Copyright (c) 2006 Multimedia systems 7 | '# (c) and code by Luis Lavena 8 | '# 9 | '# mongrel_service (native) and mongrel_service gem_pluing are licensed 10 | '# in the same terms as mongrel, please review the mongrel license at 11 | '# http://mongrel.rubyforge.org/license.html 12 | '# 13 | '################################################################## 14 | 15 | '################################################################## 16 | '# Requirements: 17 | '# - FreeBASIC 0.18. 18 | '# 19 | '################################################################## 20 | 21 | #define SERVICEFB_INCLUDE_UTILS 22 | #include once "lib/ServiceFB/ServiceFB.bi" 23 | #include once "console_process.bi" 24 | 25 | '# use for debug versions 26 | #if not defined(GEM_VERSION) 27 | #define GEM_VERSION (debug mode) 28 | #endif 29 | 30 | '# preprocessor stringize 31 | #define PPSTR(x) #x 32 | 33 | namespace mongrel_service 34 | const VERSION as string = PPSTR(GEM_VERSION) 35 | 36 | '# namespace include 37 | using fb.svc 38 | using fb.svc.utils 39 | 40 | declare function single_onInit(byref as ServiceProcess) as integer 41 | declare sub single_onStart(byref as ServiceProcess) 42 | declare sub single_onStop(byref as ServiceProcess) 43 | 44 | '# SingleMongrel 45 | type SingleMongrel 46 | declare constructor() 47 | declare destructor() 48 | 49 | '# TODO: replace for inheritance here 50 | 'declare function onInit() as integer 51 | 'declare sub onStart() 52 | 'declare sub onStop() 53 | 54 | __service as ServiceProcess 55 | __console as ConsoleProcess 56 | __child_pid as uinteger 57 | end type 58 | 59 | '# TODO: replace with inheritance here 60 | dim shared single_mongrel_ref as SingleMongrel ptr 61 | end namespace 62 | -------------------------------------------------------------------------------- /projects/mongrel_service/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | -------------------------------------------------------------------------------- /projects/mongrel_status/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v0.2.3. Signed gem. 3 | -------------------------------------------------------------------------------- /projects/mongrel_status/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_status/Manifest: -------------------------------------------------------------------------------- 1 | README 2 | Manifest 3 | LICENSE 4 | lib/mongrel_status/init.rb 5 | COPYING 6 | CHANGELOG 7 | -------------------------------------------------------------------------------- /projects/mongrel_status/README: -------------------------------------------------------------------------------- 1 | == Mongrel Status Plugin 2 | 3 | A very simple dumb status plugin that's mostly just a demonstration 4 | of how to do a plugin for mongrel. It just prints out the 5 | PID of a running mongrel server. 6 | 7 | -------------------------------------------------------------------------------- /projects/mongrel_status/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_status") do |p| 5 | p.summary = "A sample plugin that reports the status of mongrel." 6 | p.project = "mongrel" 7 | p.author = "Zed A. Shaw" 8 | p.dependencies = ['gem_plugin >=0.2.3', 'mongrel >=1.0.2'] 9 | 10 | p.need_tar_gz = false 11 | p.need_tgz = true 12 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 13 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 14 | p.require_signed = true 15 | end 16 | 17 | -------------------------------------------------------------------------------- /projects/mongrel_status/lib/mongrel_status/init.rb: -------------------------------------------------------------------------------- 1 | require 'mongrel' 2 | require 'gem_plugin' 3 | 4 | 5 | class Status < GemPlugin::Plugin "/commands" 6 | include Mongrel::Command::Base 7 | 8 | def configure 9 | options [ 10 | ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd], 11 | ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"] 12 | ] 13 | end 14 | 15 | def validate 16 | @cwd = File.expand_path(@cwd) 17 | valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd" 18 | 19 | @pid_file = File.join(@cwd,@pid_file) 20 | valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?" 21 | 22 | return @valid 23 | end 24 | 25 | 26 | def run 27 | pid = open(@pid_file) {|f| f.read } 28 | puts "Mongrel status:" 29 | puts "PID: #{pid}" 30 | end 31 | end 32 | 33 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | v0.2.2. Signed gem. 3 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/COPYING: -------------------------------------------------------------------------------- 1 | No copying restrictions/license given. -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/LICENSE: -------------------------------------------------------------------------------- 1 | Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw 2 | and contributors. You can redistribute it 3 | and/or modify it under either the terms of the GPL2 or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a) place your modifications in the Public Domain or otherwise make them 13 | Freely Available, such as by posting said modifications to Usenet or an 14 | equivalent medium, or by allowing the author to include your 15 | modifications in the software. 16 | 17 | b) use the modified software only within your corporation or 18 | organization. 19 | 20 | c) rename any non-standard executables so the names do not conflict with 21 | standard executables, which must also be provided. 22 | 23 | d) make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or executable 26 | form, provided that you do at least ONE of the following: 27 | 28 | a) distribute the executables and library files of the software, 29 | together with instructions (in the manual page or equivalent) on where 30 | to get the original distribution. 31 | 32 | b) accompany the distribution with the machine-readable source of the 33 | software. 34 | 35 | c) give non-standard executables non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d) make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under this terms. 43 | 44 | 5. The scripts and library files supplied as input to or produced as 45 | output from the software do not automatically fall under the 46 | copyright of the software, but belong to whomever generated them, 47 | and may be sold commercially, and may be aggregated with this 48 | software. 49 | 50 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 51 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | PURPOSE. 54 | 55 | 56 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/Manifest: -------------------------------------------------------------------------------- 1 | resources/defaults.yaml 2 | README 3 | Manifest 4 | LICENSE 5 | lib/mongrel_upload_progress/init.rb 6 | COPYING 7 | CHANGELOG 8 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/README: -------------------------------------------------------------------------------- 1 | == Mongrel_upload_progress GemPlugin 2 | 3 | You should document your project here. 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'echoe' 3 | 4 | Echoe.new("mongrel_upload_progress") do |p| 5 | p.summary = "The mongrel_upload_progress gemplugin" 6 | p.project = "mongrel" 7 | p.author="The Mongrel team" 8 | p.dependencies = ['mongrel >=0.3.13.3', 'gem_plugin >=0.2.1'] 9 | 10 | p.need_tar_gz = false 11 | p.need_tgz = true 12 | p.certificate_chain = ['~/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem', 13 | '~/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem'] 14 | p.require_signed = true 15 | end 16 | -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/lib/mongrel_upload_progress/init.rb: -------------------------------------------------------------------------------- 1 | require 'mongrel' 2 | require 'gem_plugin' 3 | 4 | class Upload < GemPlugin::Plugin "/handlers" 5 | include Mongrel::HttpHandlerPlugin 6 | 7 | def initialize(options = {}) 8 | @path_info = Array(options[:path_info]) 9 | @frequency = options[:frequency] || 3 10 | @request_notify = true 11 | if options[:drb] 12 | require 'drb' 13 | DRb.start_service 14 | Mongrel.const_set :Uploads, DRbObject.new(nil, options[:drb]) 15 | else 16 | Mongrel.const_set :Uploads, Mongrel::UploadProgress.new 17 | end 18 | Mongrel::Uploads.debug = true if options[:debug] 19 | end 20 | 21 | def request_begins(params) 22 | upload_notify(:add, params, params[Mongrel::Const::CONTENT_LENGTH].to_i) 23 | end 24 | 25 | def request_progress(params, clen, total) 26 | upload_notify(:mark, params, clen) 27 | end 28 | 29 | def process(request, response) 30 | upload_notify(:finish, request.params) 31 | end 32 | 33 | private 34 | def upload_notify(action, params, *args) 35 | return unless @path_info.include?(params['PATH_INFO']) && 36 | params[Mongrel::Const::REQUEST_METHOD] == 'POST' && 37 | upload_id = Mongrel::HttpRequest.query_parse(params['QUERY_STRING'])['upload_id'] 38 | if action == :mark 39 | last_checked_time = Mongrel::Uploads.last_checked(upload_id) 40 | return unless last_checked_time && Time.now - last_checked_time > @frequency 41 | end 42 | Mongrel::Uploads.send(action, upload_id, *args) 43 | Mongrel::Uploads.update_checked_time(upload_id) unless action == :finish 44 | end 45 | end 46 | 47 | # Keeps track of the status of all currently processing uploads 48 | class Mongrel::UploadProgress 49 | attr_accessor :debug 50 | def initialize 51 | @guard = Mutex.new 52 | @counters = {} 53 | end 54 | 55 | def check(upid) 56 | @counters[upid].last rescue nil 57 | end 58 | 59 | def last_checked(upid) 60 | @counters[upid].first rescue nil 61 | end 62 | 63 | def update_checked_time(upid) 64 | @guard.synchronize { @counters[upid][0] = Time.now } 65 | end 66 | 67 | def add(upid, size) 68 | @guard.synchronize do 69 | @counters[upid] = [Time.now, {:size => size, :received => 0}] 70 | puts "#{upid}: Added" if @debug 71 | end 72 | end 73 | 74 | def mark(upid, len) 75 | return unless status = check(upid) 76 | puts "#{upid}: Marking" if @debug 77 | @guard.synchronize { status[:received] = status[:size] - len } 78 | end 79 | 80 | def finish(upid) 81 | @guard.synchronize do 82 | puts "#{upid}: Finished" if @debug 83 | @counters.delete(upid) 84 | end 85 | end 86 | 87 | def list 88 | @counters.keys.sort 89 | end 90 | end -------------------------------------------------------------------------------- /projects/mongrel_upload_progress/resources/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :debug: false 3 | :upload_path: 'tmp/uploads' -------------------------------------------------------------------------------- /tasks/gem.rake: -------------------------------------------------------------------------------- 1 | require 'hoe' 2 | 3 | HOE = Hoe.spec 'mongrel' do 4 | self.rubyforge_name = 'mongrel' 5 | developer 'Zed A. Shaw', 'mongrel-users@rubyforge.org' 6 | 7 | spec_extras[:required_ruby_version] = Gem::Requirement.new('>= 1.8.6') 8 | 9 | spec_extras[:extensions] = ["ext/http11/extconf.rb"] 10 | spec_extras[:executables] = ['mongrel_rails'] 11 | 12 | extra_rdoc_files << 'LICENSE' 13 | 14 | extra_deps << ['gem_plugin', '~> 0.2.3'] 15 | extra_deps << ['daemons', '~> 1.0.10'] 16 | 17 | extra_dev_deps << ['rake-compiler', "~> 0.7.0"] 18 | 19 | clean_globs.push('test_*.log', 'log') 20 | end 21 | 22 | file "#{HOE.spec.name}.gemspec" => ['Rakefile', 'tasks/gem.rake'] do |t| 23 | puts "Generating #{t.name}" 24 | File.open(t.name, 'w') { |f| f.puts HOE.spec.to_yaml } 25 | end 26 | 27 | desc "Generate or update the standalone gemspec file for the project" 28 | task :gemspec => ["#{HOE.spec.name}.gemspec"] 29 | -------------------------------------------------------------------------------- /tasks/java.rake: -------------------------------------------------------------------------------- 1 | require 'rake/javaextensiontask' 2 | 3 | # build http11 java extension 4 | Rake::JavaExtensionTask.new('http11', HOE.spec) do |ext| 5 | ext.java_compiling do |gs| 6 | gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /tasks/native.rake: -------------------------------------------------------------------------------- 1 | # use rake-compiler for building the extension 2 | require 'rake/extensiontask' 3 | 4 | # build http11 C extension 5 | Rake::ExtensionTask.new('http11', HOE.spec) do |ext| 6 | # define target for extension (supporting fat binaries) 7 | if RUBY_PLATFORM =~ /mingw|mswin/ then 8 | RUBY_VERSION =~ /(\d+\.\d+)/ 9 | ext.lib_dir = "lib/#{$1}" 10 | else 11 | # define cross-compilation tasks when not on Windows. 12 | ext.cross_compile = true 13 | ext.cross_platform = ['i386-mswin32', 'i386-mingw32'] 14 | 15 | ext.cross_compiling do |gs| 16 | gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' } 17 | end 18 | end 19 | 20 | # cleanup versioned library directory 21 | CLEAN.include 'lib/{1.8,1.9}' 22 | end 23 | 24 | # ensure things are built prior testing 25 | task :test => [:compile] 26 | -------------------------------------------------------------------------------- /tasks/ragel.rake: -------------------------------------------------------------------------------- 1 | 2 | # the following tasks ease the build of C file from Ragel one 3 | 4 | file 'ext/http11/http11_parser.c' => ['ext/http11/http11_parser.rl'] do |t| 5 | begin 6 | sh "ragel #{t.prerequisites.last} -C -G2 -o #{t.name}" 7 | rescue 8 | fail "Could not build wrapper using Ragel (it failed or not installed?)" 9 | end 10 | end 11 | 12 | file 'ext/http11/org/jruby/mongrel/Http11Parser.java' => ['ext/http11/http11_parser.java.rl'] do |t| 13 | begin 14 | sh "ragel #{t.prerequisites.last} -J -G2 -o #{t.name}" 15 | rescue 16 | fail "Could not build wrapper using Ragel (it failed or not installed?)" 17 | end 18 | end 19 | 20 | task :ragel => (defined?(JRUBY_VERSION) ? 'ext/http11/org/jruby/mongrel/Http11Parser.java' : 'ext/http11/http11_parser.c') 21 | -------------------------------------------------------------------------------- /test/mime.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | .jpeg: image/jpeg 3 | .png: image/test 4 | -------------------------------------------------------------------------------- /test/mongrel.conf: -------------------------------------------------------------------------------- 1 | uri "/fromconf", :handler => Mongrel::Error404Handler.new("test") 2 | -------------------------------------------------------------------------------- /test/test_cgi_wrapper.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'test/testhelp' 3 | 4 | class MockHttpRequest 5 | attr_reader :body 6 | 7 | def params 8 | return { 'REQUEST_METHOD' => 'GET'} 9 | end 10 | end 11 | 12 | class CGIWrapperTest < Test::Unit::TestCase 13 | 14 | def test_set_cookies_output_cookies 15 | request = MockHttpRequest.new 16 | response = nil # not needed for this test 17 | output_headers = {} 18 | 19 | cgi = Mongrel::CGIWrapper.new(request, response) 20 | session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::MemoryStore) 21 | cgi.send_cookies(output_headers) 22 | 23 | assert(output_headers.has_key?("Set-Cookie")) 24 | assert_equal("_session_id="+session.session_id+"; path=", output_headers["Set-Cookie"]) 25 | end 26 | end -------------------------------------------------------------------------------- /test/test_command.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | class TestCommand < GemPlugin::Plugin "/commands" 10 | include Mongrel::Command::Base 11 | 12 | def configure 13 | options [ 14 | ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"], 15 | ['', '--user USER', "User to run as", :@user, nil], 16 | ["-d", "--daemonize", "Whether to run in the background or not", :@daemon, false], 17 | ["-x", "--test", "Used to let the test run failures", :@test, false], 18 | ] 19 | end 20 | 21 | def validate 22 | valid_dir? ".", "Can't validate current directory." 23 | valid_exists? "Rakefile", "Rakefile not there, test is invalid." 24 | if @test 25 | valid_exist? "BADFILE", "Yeah, badfile" 26 | valid_file? "BADFILE", "Not even a file" 27 | valid_dir? "BADDIR", "No dir here" 28 | valid? false, "Total failure" 29 | end 30 | 31 | return @valid 32 | end 33 | 34 | 35 | def run 36 | $test_command_ran = true 37 | end 38 | end 39 | 40 | class CommandTest < Test::Unit::TestCase 41 | 42 | def setup 43 | $test_command_ran = false 44 | end 45 | 46 | def teardown 47 | end 48 | 49 | def run_cmd(args) 50 | Mongrel::Command::Registry.instance.run args 51 | end 52 | 53 | def test_run_command 54 | redirect_test_io do 55 | run_cmd ["testcommand"] 56 | assert $test_command_ran, "command didn't run" 57 | end 58 | end 59 | 60 | def test_command_error 61 | redirect_test_io do 62 | run_cmd ["crapcommand"] 63 | end 64 | end 65 | 66 | def test_command_listing 67 | redirect_test_io do 68 | run_cmd ["help"] 69 | end 70 | end 71 | 72 | def test_options 73 | redirect_test_io do 74 | run_cmd ["testcommand","-h"] 75 | run_cmd ["testcommand","--help"] 76 | run_cmd ["testcommand","-e","test","-d","--user"] 77 | end 78 | end 79 | 80 | def test_version 81 | redirect_test_io do 82 | run_cmd ["testcommand", "--version"] 83 | end 84 | end 85 | 86 | end 87 | -------------------------------------------------------------------------------- /test/test_configurator.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | $test_plugin_fired = 0 10 | 11 | class TestPlugin < GemPlugin::Plugin "/handlers" 12 | include Mongrel::HttpHandlerPlugin 13 | 14 | def process(request, response) 15 | $test_plugin_fired += 1 16 | end 17 | end 18 | 19 | 20 | class Sentinel < GemPlugin::Plugin "/handlers" 21 | include Mongrel::HttpHandlerPlugin 22 | 23 | def process(request, response) 24 | raise "This Sentinel plugin shouldn't run." 25 | end 26 | end 27 | 28 | 29 | class ConfiguratorTest < Test::Unit::TestCase 30 | 31 | def test_base_handler_config 32 | @config = nil 33 | 34 | redirect_test_io do 35 | @config = Mongrel::Configurator.new :host => "localhost" do 36 | listener :port => 4501 do 37 | # 2 in front should run, but the sentinel shouldn't since dirhandler processes the request 38 | uri "/", :handler => plugin("/handlers/testplugin") 39 | uri "/", :handler => plugin("/handlers/testplugin") 40 | uri "/", :handler => Mongrel::DirHandler.new(".") 41 | uri "/", :handler => plugin("/handlers/testplugin") 42 | 43 | uri "/test", :handler => plugin("/handlers/testplugin") 44 | uri "/test", :handler => plugin("/handlers/testplugin") 45 | uri "/test", :handler => Mongrel::DirHandler.new(".") 46 | uri "/test", :handler => plugin("/handlers/testplugin") 47 | 48 | debug "/" 49 | setup_signals 50 | 51 | run_config(File.dirname(__FILE__) + "/../test/mongrel.conf") 52 | load_mime_map(File.dirname(__FILE__) + "/../test/mime.yaml") 53 | 54 | run 55 | end 56 | end 57 | end 58 | 59 | # pp @config.listeners.values.first.classifier.routes 60 | 61 | @config.listeners.each do |host,listener| 62 | assert listener.classifier.uris.length == 3, "Wrong number of registered URIs" 63 | assert listener.classifier.uris.include?("/"), "/ not registered" 64 | assert listener.classifier.uris.include?("/test"), "/test not registered" 65 | end 66 | 67 | res = Net::HTTP.get(URI.parse('http://localhost:4501/test')) 68 | assert res != nil, "Didn't get a response" 69 | assert $test_plugin_fired == 3, "Test filter plugin didn't run 3 times." 70 | 71 | redirect_test_io do 72 | res = Net::HTTP.get(URI.parse('http://localhost:4501/')) 73 | 74 | assert res != nil, "Didn't get a response" 75 | assert $test_plugin_fired == 6, "Test filter plugin didn't run 6 times." 76 | end 77 | 78 | redirect_test_io do 79 | @config.stop(false, true) 80 | end 81 | 82 | assert_raise Errno::EBADF, Errno::ECONNREFUSED do 83 | res = Net::HTTP.get(URI.parse("http://localhost:4501/")) 84 | end 85 | end 86 | 87 | end 88 | -------------------------------------------------------------------------------- /test/test_debug.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | require 'mongrel/debug' 9 | 10 | class MongrelDbgTest < Test::Unit::TestCase 11 | 12 | def test_tracing_to_log 13 | FileUtils.rm_rf "log/mongrel_debug" 14 | 15 | MongrelDbg::configure 16 | out = StringIO.new 17 | 18 | MongrelDbg::begin_trace(:rails) 19 | MongrelDbg::trace(:rails, "Good stuff") 20 | MongrelDbg::end_trace(:rails) 21 | 22 | assert File.exist?("log/mongrel_debug"), "Didn't make logging directory" 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /test/test_redirect_handler.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | class RedirectHandlerTest < Test::Unit::TestCase 10 | 11 | def setup 12 | redirect_test_io do 13 | @server = Mongrel::HttpServer.new('127.0.0.1', 9998) 14 | end 15 | @server.run 16 | @client = Net::HTTP.new('127.0.0.1', 9998) 17 | end 18 | 19 | def teardown 20 | @server.stop(true) 21 | end 22 | 23 | def test_simple_redirect 24 | tester = Mongrel::RedirectHandler.new('/yo') 25 | @server.register("/test", tester) 26 | 27 | sleep(1) 28 | res = @client.request_get('/test') 29 | assert res != nil, "Didn't get a response" 30 | assert_equal ['/yo'], res.get_fields('Location') 31 | end 32 | 33 | def test_rewrite 34 | tester = Mongrel::RedirectHandler.new(/(\w+)/, '+\1+') 35 | @server.register("/test", tester) 36 | 37 | sleep(1) 38 | res = @client.request_get('/test/something') 39 | assert_equal ['/+test+/+something+'], res.get_fields('Location') 40 | end 41 | 42 | end 43 | 44 | 45 | -------------------------------------------------------------------------------- /test/test_request_progress.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | class UploadBeginHandler < Mongrel::HttpHandler 10 | attr_reader :request_began, :request_progressed, :request_processed 11 | 12 | def initialize 13 | @request_notify = true 14 | end 15 | 16 | def reset 17 | @request_began = false 18 | @request_progressed = false 19 | @request_processed = false 20 | end 21 | 22 | def request_begins(params) 23 | @request_began = true 24 | end 25 | 26 | def request_progress(params,len,total) 27 | @request_progressed = true 28 | end 29 | 30 | def process(request, response) 31 | @request_processed = true 32 | response.start do |head,body| 33 | body.write("test") 34 | end 35 | end 36 | 37 | end 38 | 39 | class RequestProgressTest < Test::Unit::TestCase 40 | def setup 41 | redirect_test_io do 42 | @server = Mongrel::HttpServer.new("127.0.0.1", 9998) 43 | end 44 | @handler = UploadBeginHandler.new 45 | @server.register("/upload", @handler) 46 | @server.run 47 | end 48 | 49 | def teardown 50 | @server.stop(true) 51 | end 52 | 53 | def test_begin_end_progress 54 | Net::HTTP.get("localhost", "/upload", 9998) 55 | assert @handler.request_began 56 | assert @handler.request_progressed 57 | assert @handler.request_processed 58 | end 59 | 60 | def call_and_assert_handlers_in_turn(handlers) 61 | # reset all handlers 62 | handlers.each { |h| h.reset } 63 | 64 | # make the call 65 | Net::HTTP.get("localhost", "/upload", 9998) 66 | 67 | # assert that each one was fired 68 | handlers.each { |h| 69 | assert h.request_began && h.request_progressed && h.request_processed, 70 | "Callbacks NOT fired for #{h}" 71 | } 72 | end 73 | 74 | def test_more_than_one_begin_end_progress 75 | handlers = [@handler] 76 | 77 | second = UploadBeginHandler.new 78 | @server.register("/upload", second) 79 | handlers << second 80 | call_and_assert_handlers_in_turn(handlers) 81 | 82 | # check three handlers 83 | third = UploadBeginHandler.new 84 | @server.register("/upload", third) 85 | handlers << third 86 | call_and_assert_handlers_in_turn(handlers) 87 | 88 | # remove handlers to make sure they've all gone away 89 | @server.unregister("/upload") 90 | handlers.each { |h| h.reset } 91 | Net::HTTP.get("localhost", "/upload", 9998) 92 | handlers.each { |h| 93 | assert !h.request_began && !h.request_progressed && !h.request_processed 94 | } 95 | 96 | # re-register upload to the state before this test 97 | @server.register("/upload", @handler) 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /test/test_response.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | include Mongrel 10 | 11 | class ResponseTest < Test::Unit::TestCase 12 | 13 | def test_response_headers 14 | out = StringIO.new 15 | resp = HttpResponse.new(out) 16 | resp.status = 200 17 | resp.header["Accept"] = "text/plain" 18 | resp.header["X-Whatever"] = "stuff" 19 | resp.body.write("test") 20 | resp.finished 21 | 22 | assert out.length > 0, "output didn't have data" 23 | end 24 | 25 | def test_response_200 26 | io = StringIO.new 27 | resp = HttpResponse.new(io) 28 | resp.start do |head,out| 29 | head["Accept"] = "text/plain" 30 | out.write("tested") 31 | out.write("hello!") 32 | end 33 | 34 | resp.finished 35 | assert io.length > 0, "output didn't have data" 36 | end 37 | 38 | def test_response_duplicate_header_squash 39 | io = StringIO.new 40 | resp = HttpResponse.new(io) 41 | resp.start do |head,out| 42 | head["Content-Length"] = 30 43 | head["Content-Length"] = 0 44 | end 45 | 46 | resp.finished 47 | 48 | assert_equal io.length, 95, "too much output" 49 | end 50 | 51 | 52 | def test_response_some_duplicates_allowed 53 | allowed_duplicates = ["Set-Cookie", "Set-Cookie2", "Warning", "WWW-Authenticate"] 54 | io = StringIO.new 55 | resp = HttpResponse.new(io) 56 | resp.start do |head,out| 57 | allowed_duplicates.each do |dup| 58 | 10.times do |i| 59 | head[dup] = i 60 | end 61 | end 62 | end 63 | 64 | resp.finished 65 | 66 | assert_equal io.length, 734, "wrong amount of output" 67 | end 68 | 69 | def test_response_404 70 | io = StringIO.new 71 | 72 | resp = HttpResponse.new(io) 73 | resp.start(404) do |head,out| 74 | head['Accept'] = "text/plain" 75 | out.write("NOT FOUND") 76 | end 77 | 78 | resp.finished 79 | assert io.length > 0, "output didn't have data" 80 | end 81 | 82 | def test_response_file 83 | contents = "PLAIN TEXT\r\nCONTENTS\r\n" 84 | require 'tempfile' 85 | tmpf = Tempfile.new("test_response_file") 86 | tmpf.binmode 87 | tmpf.write(contents) 88 | tmpf.rewind 89 | 90 | io = StringIO.new 91 | resp = HttpResponse.new(io) 92 | resp.start(200) do |head,out| 93 | head['Content-Type'] = 'text/plain' 94 | resp.send_header 95 | resp.send_file(tmpf.path) 96 | end 97 | io.rewind 98 | tmpf.close 99 | 100 | assert io.length > 0, "output didn't have data" 101 | assert io.read[-contents.length..-1] == contents, "output doesn't end with file payload" 102 | end 103 | 104 | def test_response_with_custom_reason 105 | reason = "You made a bad request" 106 | io = StringIO.new 107 | resp = HttpResponse.new(io) 108 | resp.start(400, false, reason) { |head,out| } 109 | resp.finished 110 | 111 | io.rewind 112 | assert_match(/.* #{reason}$/, io.readline.chomp, "wrong custom reason phrase") 113 | end 114 | 115 | def test_response_with_default_reason 116 | code = 400 117 | io = StringIO.new 118 | resp = HttpResponse.new(io) 119 | resp.start(code) { |head,out| } 120 | resp.finished 121 | 122 | io.rewind 123 | assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, io.readline.chomp, "wrong default reason phrase") 124 | end 125 | 126 | end 127 | 128 | -------------------------------------------------------------------------------- /test/test_stats.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | class StatsTest < Test::Unit::TestCase 10 | 11 | def test_sampling_speed 12 | out = StringIO.new 13 | 14 | s = Mongrel::Stats.new("test") 15 | t = Mongrel::Stats.new("time") 16 | 17 | 100.times { s.sample(rand(20)); t.tick } 18 | 19 | s.dump("FIRST", out) 20 | t.dump("FIRST", out) 21 | 22 | old_mean = s.mean 23 | old_sd = s.sd 24 | 25 | s.reset 26 | t.reset 27 | 100.times { s.sample(rand(30)); t.tick } 28 | 29 | s.dump("SECOND", out) 30 | t.dump("SECOND", out) 31 | assert_not_equal old_mean, s.mean 32 | assert_not_equal old_mean, s.sd 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /test/test_ws.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | require 'test/testhelp' 8 | 9 | include Mongrel 10 | 11 | class TestHandler < Mongrel::HttpHandler 12 | attr_reader :ran_test 13 | 14 | def process(request, response) 15 | @ran_test = true 16 | response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n") 17 | end 18 | end 19 | 20 | 21 | class WebServerTest < Test::Unit::TestCase 22 | 23 | def setup 24 | @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n" 25 | 26 | redirect_test_io do 27 | # We set num_processors=1 so that we can test the reaping code 28 | @server = HttpServer.new("127.0.0.1", 9998, num_processors=1) 29 | end 30 | 31 | @tester = TestHandler.new 32 | @server.register("/test", @tester) 33 | redirect_test_io do 34 | @server.run 35 | end 36 | end 37 | 38 | def teardown 39 | redirect_test_io do 40 | @server.stop(true) 41 | end 42 | end 43 | 44 | def test_simple_server 45 | hit(['http://localhost:9998/test']) 46 | assert @tester.ran_test, "Handler didn't really run" 47 | end 48 | 49 | 50 | def do_test(string, chunk, close_after=nil, shutdown_delay=0) 51 | # Do not use instance variables here, because it needs to be thread safe 52 | socket = TCPSocket.new("127.0.0.1", 9998); 53 | request = StringIO.new(string) 54 | chunks_out = 0 55 | 56 | while data = request.read(chunk) 57 | chunks_out += socket.write(data) 58 | socket.flush 59 | sleep 0.2 60 | if close_after and chunks_out > close_after 61 | socket.close 62 | sleep 1 63 | end 64 | end 65 | sleep(shutdown_delay) 66 | socket.write(" ") # Some platforms only raise the exception on attempted write 67 | socket.flush 68 | end 69 | 70 | def test_trickle_attack 71 | do_test(@valid_request, 3) 72 | end 73 | 74 | def test_close_client 75 | assert_raises IOError do 76 | do_test(@valid_request, 10, 20) 77 | end 78 | end 79 | 80 | def test_bad_client 81 | redirect_test_io do 82 | do_test("GET /test HTTP/BAD", 3) 83 | end 84 | end 85 | 86 | def test_header_is_too_long 87 | redirect_test_io do 88 | long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n" 89 | assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do 90 | do_test(long, long.length/2, 10) 91 | end 92 | end 93 | end 94 | 95 | def test_num_processors_overload 96 | redirect_test_io do 97 | assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do 98 | tests = [ 99 | Thread.new { do_test(@valid_request, 1) }, 100 | Thread.new { do_test(@valid_request, 10) }, 101 | ] 102 | 103 | tests.each {|t| t.join} 104 | end 105 | end 106 | end 107 | 108 | def test_file_streamed_request 109 | body = "a" * (Mongrel::Const::MAX_BODY * 2) 110 | long = "GET /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body 111 | do_test(long, Mongrel::Const::CHUNK_SIZE * 2 -400) 112 | end 113 | 114 | end 115 | 116 | -------------------------------------------------------------------------------- /test/testhelp.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Zed A. Shaw 2 | # You can redistribute it and/or modify it under the same terms as Ruby. 3 | # 4 | # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 | # for more information. 6 | 7 | 8 | HERE = File.dirname(__FILE__) 9 | %w(lib ext bin test).each do |dir| 10 | $LOAD_PATH.unshift "#{HERE}/../#{dir}" 11 | end 12 | 13 | require 'rubygems' 14 | require 'test/unit' 15 | require 'net/http' 16 | require 'timeout' 17 | require 'cgi/session' 18 | require 'fileutils' 19 | require 'benchmark' 20 | require 'digest/sha1' 21 | require 'uri' 22 | require 'stringio' 23 | require 'pp' 24 | 25 | require 'mongrel' 26 | require 'mongrel/stats' 27 | 28 | if ENV['DEBUG'] 29 | require 'ruby-debug' 30 | Debugger.start 31 | end 32 | 33 | def redirect_test_io 34 | orig_err = STDERR.dup 35 | orig_out = STDOUT.dup 36 | STDERR.reopen("test_stderr.log") 37 | STDOUT.reopen("test_stdout.log") 38 | 39 | begin 40 | yield 41 | ensure 42 | STDERR.reopen(orig_err) 43 | STDOUT.reopen(orig_out) 44 | end 45 | end 46 | 47 | # Either takes a string to do a get request against, or a tuple of [URI, HTTP] where 48 | # HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.) 49 | def hit(uris) 50 | results = [] 51 | uris.each do |u| 52 | res = nil 53 | 54 | if u.kind_of? String 55 | res = Net::HTTP.get(URI.parse(u)) 56 | else 57 | url = URI.parse(u[0]) 58 | res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) } 59 | end 60 | 61 | assert res != nil, "Didn't get a response: #{u}" 62 | results << res 63 | end 64 | 65 | return results 66 | end 67 | 68 | # Platform check helper ;-) 69 | def windows? 70 | @windows ||= RbConfig::CONFIG['host_os'] =~ /mingw|mswin/ 71 | end 72 | -------------------------------------------------------------------------------- /tools/trickletest.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | require 'stringio' 3 | 4 | def do_test(st, chunk) 5 | s = TCPSocket.new('127.0.0.1',ARGV[0].to_i); 6 | req = StringIO.new(st) 7 | nout = 0 8 | randstop = rand(st.length / 10) 9 | STDERR.puts "stopping after: #{randstop}" 10 | 11 | begin 12 | while data = req.read(chunk) 13 | nout += s.write(data) 14 | s.flush 15 | sleep 0.1 16 | if nout > randstop 17 | STDERR.puts "BANG! after #{nout} bytes." 18 | break 19 | end 20 | end 21 | rescue Object => e 22 | STDERR.puts "ERROR: #{e}" 23 | ensure 24 | s.close 25 | end 26 | end 27 | 28 | content = "-" * (1024 * 240) 29 | st = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\nContent-Length: #{content.length}\r\n\r\n#{content}" 30 | 31 | puts "length: #{content.length}" 32 | 33 | threads = [] 34 | ARGV[1].to_i.times do 35 | t = Thread.new do 36 | size = 100 37 | puts ">>>> #{size} sized chunks" 38 | do_test(st, size) 39 | end 40 | 41 | t.abort_on_exception = true 42 | threads << t 43 | end 44 | 45 | threads.each {|t| t.join} 46 | --------------------------------------------------------------------------------