OSDN Git Service

Replaced mongrel with thin
authorAkihiro Ono <a-ono@users.sourceforge.jp>
Sat, 9 Jul 2011 15:22:29 +0000 (00:22 +0900)
committerAkihiro Ono <a-ono@users.sourceforge.jp>
Sat, 9 Jul 2011 15:22:29 +0000 (00:22 +0900)
439 files changed:
.gitignore
jenkins/.gitignore
redmine
ruby/bin/thin [moved from ruby/bin/mongrel_rails with 60% similarity]
ruby/bin/thin.bat [moved from ruby/bin/mongrel_rails.bat with 58% similarity]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/LICENSE [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/README [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Rakefile [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Releases [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/TODO [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call_monitor.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/daemonize/daemonize.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_crash.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exit.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_hanging.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_keep_pid_files.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_monitor.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_multiple.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_normal.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_ontop.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_optionparser.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_multiple.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_simple.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_crashing.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_exiting.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_hanging.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application_group.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/change_privilege.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/cmdline.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/controller.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/daemonize.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/etc_extension.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/exceptions.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/monitor.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pid.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidfile.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidmem.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/setup.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/.gitignore [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/README [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/Rakefile [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/COPYING [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/ChangeLog [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/DEFERRABLES [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/EPOLL [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/GNU [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/INSTALL [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/KEYBOARD [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LEGAL [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LIGHTWEIGHT_CONCURRENCY [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/PURE_RUBY [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/RELEASE_NOTES [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SMTP [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SPAWNED_PROCESSES [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/TODO [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/eventmachine.gemspec [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_channel.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_queue.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/helper.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cmain.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cplusplus.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine_cpp.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/extconf.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/extconf.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/rubymain.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/kb.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/pipe.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/project.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/rubymain.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.cpp [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.h [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.classpath [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.project [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactor.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactorException.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableChannel.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableDatagramChannel.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableSocketChannel.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Application.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Connection.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/ConnectionFactory.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/PeriodicTimer.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Timer.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ApplicationTest.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ConnectTest.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/EMTest.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestDatagrams.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestServers.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestTimers.java [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/buftok.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/callback.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/channel.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/connection.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/deferrable.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/file_watch.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/future.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/messages.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/process_watch.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/processes.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/header_and_content.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient2.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/line_and_text.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/linetext2.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/memcache.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/object_protocol.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/postgres3.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/saslauth.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpclient.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpserver.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/socks4.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/stomp.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/tcptest.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/queue.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/spawnable.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/streamer.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/timers.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/version.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/eventmachine.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/callback.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/container.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/factory.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/protocol.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/reactor.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/jeventmachine.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/pr_eventmachine.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/setup.rb [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/setup.rb with 95% similarity]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tasks/cpp.rake_example [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.crt [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.key [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_attach.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_basic.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_channel.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_connection_count.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_defer.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_epoll.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_error_handler.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_errors.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_exc.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_file_watch.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_futures.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_get_sock_opt.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_handler_check.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_hc.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient2.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_inactivity_timeout.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_kb.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp2.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_next_tick.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_object_protocol.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pause.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pending_connect_timeout.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_process_watch.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_processes.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_proxy_connection.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pure.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_queue.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_running.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_sasl.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_send_file.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_servers.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpclient.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpserver.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_spawn.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_args.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_methods.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_verify.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_timers.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ud.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/testem.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/web/whatis [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/.require_paths [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/CHANGELOG [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/COPYING [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/LICENSE [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/Manifest [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/README [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/TODO [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/bin/mongrel_rails [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/builder.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/README [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/blog.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/tepee.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/httpd.conf [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mime.yaml [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel.conf [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_ctrl.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_service.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/monitrc [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/random_thrash.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/simpletest.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/webrick_compare.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/extconf.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.java.rl [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/Http11Service.java [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11.java [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11Parser.java [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/http11.so [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/camping.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/cgi.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/command.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/configurator.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/const.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/debug.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/gems.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/handlers.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/header_out.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_request.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_response.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/init.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/mime_types.yml [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/rails.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/stats.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/tcphack.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/uri_classifier.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel-public_cert.pem [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel.gemspec [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mime.yaml [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mongrel.conf [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_cgi_wrapper.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_command.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_conditional.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_configurator.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_debug.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_handlers.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_http11.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_redirect_handler.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_request_progress.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_response.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_stats.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_uriclassifier.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_ws.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/testhelp.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/tools/trickletest.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/CHANGELOG [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/COPYING [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/LICENSE [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/Manifest [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/README [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/TODO [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/bin/mongrel_service.exe [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bas [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bas [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_internals.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_utils_internals.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/mongrel_service/init.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/mongrel_service.gemspec [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/_debug.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bas [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bas [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bi [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/resources/defaults.yaml [deleted file]
ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/tools/freebasic.rb [deleted file]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/CHANGELOG [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/COPYING [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/README [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/Rakefile [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/abc [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/benchmarker.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/runner [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/bin/thin [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/adapter.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_app.ru [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_chat.ru [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_tailer.ru [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/config.ru [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_sockets [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_unixsock [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/myapp.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/ramaze.ru [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin.god [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.erb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.readme.txt [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/vlad.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/common.rl [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser_common.rl with 86% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/ext_help.h [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/ext_help.h with 100% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/extconf.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/parser.c [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.c with 67% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/parser.h [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.h with 100% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/parser.rl [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.rl with 77% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/thin.c [moved from ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11.c with 66% similarity]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.8/thin_parser.so [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.9/thin_parser.so [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/loader.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/rails.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/base.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/swiftiply_client.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/tcp_server.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/unix_server.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/command.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/connection.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/cluster.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/controller.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.sh.erb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/daemonizing.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/headers.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/logging.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/request.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/response.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/runner.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/server.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.html.erb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/statuses.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/version.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/swiftiply_client_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/tcp_server_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/unix_server_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/command_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/cluster.yml [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/single.yml [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/connection_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/cluster_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/controller_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/service_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/daemonizing_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/headers_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/logging_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/request_perf_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/response_perf_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/server_perf_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/loader_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/rails_adapter_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/application.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/simple_controller.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/helpers/application_helper.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/views/simple/index.html.erb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/boot.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environment.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/development.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/production.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/test.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/inflections.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/mime_types.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/routes.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/404.html [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/422.html [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/500.html [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.cgi [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.fcgi [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/favicon.ico [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/images/rails.png [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/index.html [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/application.js [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/controls.js [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/dragdrop.js [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/effects.js [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/prototype.js [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/robots.txt [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/about [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/console [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/destroy [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/generate [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/benchmarker [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/profiler [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/request [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/plugin [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/inspector [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/reaper [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/spawner [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/runner [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/server [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/mongrel_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/parser_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/persistent_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/processing_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/response_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/runner_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/builder_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/pipelining_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/robustness_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/stopping_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply.yml [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/tcp_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/threaded_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/unix_socket_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server_spec.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/spec_helper.rb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/announce.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/deploy.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/email.erb [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/gem.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/rdoc.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/site.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/spec.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/stats.rake [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/specifications/daemons-1.1.4.gemspec [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/specifications/eventmachine-0.12.10-x86-mswin32-60.gemspec [new file with mode: 0644]
ruby/lib/ruby/gems/1.8/specifications/mongrel-1.1.5-x86-mswin32-60.gemspec [deleted file]
ruby/lib/ruby/gems/1.8/specifications/mongrel_service-0.3.4-x86-mswin32.gemspec [deleted file]
ruby/lib/ruby/gems/1.8/specifications/thin-1.2.11-x86-mswin32.gemspec [new file with mode: 0644]
script/mongrel_rails_env [deleted file]
script/service.bat
script/service/jenkins.exe [moved from jenkins/jenkins.exe with 100% similarity]
script/service/redmine.exe [new file with mode: 0644]
script/service/wrapper.bat [new file with mode: 0644]
script/setenv.bat
script/wrapper.bat [deleted file]
template/jenkins/jenkins.xml.erb [deleted file]
template/script/service/jenkins.xml.erb [new file with mode: 0644]
template/script/service/redmine.xml.erb [new file with mode: 0644]

index f590ee6..6b5a18c 100644 (file)
@@ -44,5 +44,4 @@ opends/db/userRoot
 opends/lib/set-java-home.bat
 opends/locks/*.lock
 opends/logs/
-ruby/bin/mongrel.log
-ruby/bin/mongrel_service.exe
+script/service/*.xml
index 9c23014..8b4d921 100644 (file)
@@ -1,4 +1,3 @@
-jenkins.xml
 home/*.log
 home/*.xml
 home/*.bak
diff --git a/redmine b/redmine
index 7228716..aff1d2d 160000 (submodule)
--- a/redmine
+++ b/redmine
@@ -1 +1 @@
-Subproject commit 722871657ad54b9741b6758de55d0dd6beb57f09
+Subproject commit aff1d2db98350826bfd99327516479bc84aa5a19
similarity index 60%
rename from ruby/bin/mongrel_rails
rename to ruby/bin/thin
index e924a54..28b50cf 100644 (file)
@@ -2,7 +2,7 @@
 #\r
 # This file was generated by RubyGems.\r
 #\r
-# The application 'mongrel' is installed as part of a gem, and\r
+# The application 'thin' is installed as part of a gem, and\r
 # this file is here to facilitate running it.\r
 #\r
 \r
@@ -15,5 +15,5 @@ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
   ARGV.shift\r
 end\r
 \r
-gem 'mongrel', version\r
-load Gem.bin_path('mongrel', 'mongrel_rails', version)\r
+gem 'thin', version\r
+load Gem.bin_path('thin', 'thin', version)\r
similarity index 58%
rename from ruby/bin/mongrel_rails.bat
rename to ruby/bin/thin.bat
index 61f8b34..9acf2c5 100644 (file)
@@ -1,6 +1,6 @@
 @ECHO OFF\r
 IF NOT "%~f0" == "~f0" GOTO :WinNT\r
-@"ruby.exe" -S mongrel_rails %1 %2 %3 %4 %5 %6 %7 %8 %9\r
+@"ruby.exe" -S thin %1 %2 %3 %4 %5 %6 %7 %8 %9\r
 GOTO :EOF\r
 :WinNT\r
 @"ruby.exe" "%~dpn0" %*\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/LICENSE b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/LICENSE
new file mode 100644 (file)
index 0000000..6fa746d
--- /dev/null
@@ -0,0 +1,29 @@
+Copyright (c) 2005-2011 Thomas Uehlinger
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+This license does not apply to daemonize.rb, which is was written by 
+Travis Whitton und published under the following license:
+
+The Daemonize extension module is copywrited free software by Travis Whitton
+<whitton@atlantic.net>. You can redistribute it under the terms specified in
+the COPYING file of the Ruby distribution.
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/README b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/README
new file mode 100644 (file)
index 0000000..5c4f41f
--- /dev/null
@@ -0,0 +1,224 @@
+= Daemons Version 1.1.4
+
+(See Releases for release-specific information)
+
+== What is Daemons?
+
+Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server) 
+to be <i>run as a daemon</i> and to be <i>controlled by simple start/stop/restart commands</i>.
+
+If you want, you can also use daemons to <i>run blocks of ruby code in a daemon process</i> and to control
+these processes from the main application.
+
+Besides this basic functionality, daemons offers many advanced features like <i>exception backtracing</i> 
+and logging (in case your ruby script crashes) and <i>monitoring</i> and automatic restarting of your processes
+if they crash.
+
+Daemons includes the <tt>daemonize.rb</tt> script written by <i>Travis Whitton</i> to do the daemonization
+process.
+
+== Basic Usage
+
+You can use Daemons in four different ways:
+
+=== 1. Create wrapper scripts for your server scripts or applications
+
+Layout: suppose you have your self-written server <tt>myserver.rb</tt>:
+
+  # this is myserver.rb
+  # it does nothing really useful at the moment
+  
+  loop do
+    sleep(5)
+  end
+  
+To use <tt>myserver.rb</tt> in a production environment, you need to be able to
+run <tt>myserver.rb</tt> in the _background_ (this means detach it from the console, fork it
+in the background, release all directories and file descriptors).
+
+Just create <tt>myserver_control.rb</tt> like this:
+
+  # this is myserver_control.rb
+  
+  require 'rubygems'        # if you use RubyGems
+  require 'daemons'
+  
+  Daemons.run('myserver.rb')
+  
+And use it like this from the console:
+
+  $ ruby myserver_control.rb start
+      (myserver.rb is now running in the background)
+  $ ruby myserver_control.rb restart
+      (...)
+  $ ruby myserver_control.rb stop
+  
+For testing purposes you can even run <tt>myserver.rb</tt> <i>without forking</i> in the background:
+
+  $ ruby myserver_control.rb run
+
+An additional nice feature of Daemons is that you can pass <i>additional arguments</i> to the script that 
+should be daemonized by seperating them by two _hyphens_:
+  
+  $ ruby myserver_control.rb start -- --file=anyfile --a_switch another_argument
+  
+
+=== 2. Create wrapper scripts that include your server procs
+
+Layout: suppose you have some code you want to run in the background and control that background process
+from a script:
+
+  # this is your code
+  # it does nothing really useful at the moment
+  
+  loop do
+    sleep(5)
+  end
+  
+To run this code as a daemon create <tt>myproc_control.rb</tt> like this and include your code:
+
+  # this is myproc_control.rb
+  
+  require 'rubygems'        # if you use RubyGems
+  require 'daemons'
+  
+  Daemons.run_proc('myproc.rb') do
+    loop do
+      sleep(5)
+    end
+  end
+  
+And use it like this from the console:
+
+  $ ruby myproc_control.rb start
+      (myproc.rb is now running in the background)
+  $ ruby myproc_control.rb restart
+      (...)
+  $ ruby myproc_control.rb stop
+  
+For testing purposes you can even run <tt>myproc.rb</tt> <i>without forking</i> in the background:
+
+  $ ruby myproc_control.rb run
+  
+=== 3. Control a bunch of daemons from another application
+
+Layout: you have an application <tt>my_app.rb</tt> that wants to run a bunch of 
+server tasks as daemon processes.
+
+  # this is my_app.rb
+  
+  require 'rubygems'        # if you use RubyGems
+  require 'daemons'
+  
+  task1 = Daemons.call(:multiple => true) do
+    # first server task
+    
+    loop {
+      conn = accept_conn()
+      serve(conn)
+    }
+  end
+  
+  task2 = Daemons.call do
+    # second server task
+    
+    loop {
+      something_different()
+    }
+  end
+  
+  # the parent process continues to run
+  
+  # we can even control our tasks, for example stop them
+  task1.stop
+  task2.stop
+  
+  exit
+  
+=== 4. Daemonize the currently running process
+
+Layout: you have an application <tt>my_daemon.rb</tt> that wants to run as a daemon
+(but without the ability to be controlled by daemons via start/stop commands)
+
+  # this is my_daemons.rb
+  
+  require 'rubygems'        # if you use RubyGems
+  require 'daemons'
+  
+  # Initialize the app while we're not a daemon
+  init()
+  
+  # Become a daemon
+  Daemons.daemonize
+  
+  # The server loop
+  loop {
+    conn = accept_conn()
+    serve(conn)
+  }
+
+  
+<b>For further documentation, refer to the module documentation of Daemons.</b>
+
+  
+== Download and Installation
+
+*Download*: just go to http://rubyforge.org/projects/daemons/
+
+Installation *with* RubyGems:
+  $ su
+  # gem install daemons
+  
+Installation *without* RubyGems:
+  $ tar xfz daemons-x.x.x.tar.gz
+  $ cd daemons-x.x.x
+  $ su
+  # ruby setup.rb
+  
+== Documentation
+
+For further documentation, refer to the module documentation of Daemons (click on Daemons).
+
+The RDoc documentation is also online at http://daemons.rubyforge.org
+
+
+== Author
+
+Written 2005-2010 by Thomas Uehlinger <mailto:th.uehlinger@gmx.ch>.
+Anonymous SVN checkout is available with "svn checkout http://daemons.rubyforge.org/svn/".
+
+== License
+
+Copyright (c) 2005-2010 Thomas Uehlinger
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+This license does not apply to daemonize.rb, which is was written by 
+Travis Whitton und published under the following license:
+
+The Daemonize extension module is copywrited free software by Travis Whitton
+<whitton@atlantic.net>. You can redistribute it under the terms specified in
+the COPYING file of the Ruby distribution.
+
+== Feedback and other resources
+
+At http://rubyforge.org/projects/daemons.
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Rakefile b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Rakefile
new file mode 100644 (file)
index 0000000..4356a6d
--- /dev/null
@@ -0,0 +1,90 @@
+require 'rubygems'
+#Gem::manage_gems
+
+require 'rake/gempackagetask'
+#require 'rake/testtask'
+require 'rake/packagetask'
+require 'rake/rdoctask'
+
+$LOAD_PATH << './lib'
+require 'daemons'
+
+
+PKG_NAME = "daemons"
+
+PKG_FILES = FileList[
+  "Rakefile", "Releases", "TODO", "README", "LICENSE",
+  "setup.rb",
+  "lib/**/*.rb",
+  #"test/**/*",
+  "examples/**/*.rb"
+]
+#PKG_FILES.exclude(%r(^test/tmp/.+))
+PKG_FILES.exclude(%r(\.pid$))
+PKG_FILES.exclude(%r(\.log$))
+PKG_FILES.exclude(%r(\.output$))
+PKG_FILES.exclude(%r(\.txt$))
+
+spec = Gem::Specification.new do |s|
+  s.name = PKG_NAME
+  s.version = Daemons::VERSION
+  s.author = "Thomas Uehlinger"
+  s.email = "th.uehlinger@gmx.ch"
+  s.rubyforge_project = "daemons"
+  s.homepage = "http://daemons.rubyforge.org"
+  s.platform  = Gem::Platform::RUBY
+  s.summary = "A toolkit to create and control daemons in different ways"
+  s.description = <<-EOF
+    Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server) 
+    to be run as a daemon and to be controlled by simple start/stop/restart commands.
+    
+    You can also call blocks as daemons and control them from the parent or just daemonize the current
+    process.
+    
+    Besides this basic functionality, daemons offers many advanced features like exception 
+    backtracing and logging (in case your ruby script crashes) and monitoring and automatic
+    restarting of your processes if they crash.
+  EOF
+    
+  #s.files = FileList["{test,lib}/**/*"].exclude("rdoc").to_a
+  s.files = PKG_FILES
+  s.require_path = "lib"
+  s.autorequire = "daemons"
+  s.has_rdoc = true
+  s.extra_rdoc_files = ["README", "Releases", "TODO"]
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+  pkg.need_tar = true
+end
+
+
+#Rake::PackageTask.new("package") do |p|
+#  p.name = PKG_NAME
+#  p.version = Daemons::VERSION
+#  p.need_tar = true
+#  p.need_zip = true
+#  p.package_files = PKG_FILES
+#end
+
+task :default => [:package]
+
+desc 'Show information about the gem.'
+task :debug_gem do
+  puts spec.to_ruby
+end
+
+task :upload do
+  sh "scp -r html/* uehli@rubyforge.org:/var/www/gforge-projects/daemons"
+end
+
+
+desc "Create the RDOC html files"
+rd = Rake::RDocTask.new("rdoc") { |rdoc|
+  rdoc.rdoc_dir = 'html'
+  rdoc.title    = "Daemons"
+  rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
+  rdoc.rdoc_files.include('README', 'TODO', 'Releases')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Releases b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/Releases
new file mode 100644 (file)
index 0000000..0a9c849
--- /dev/null
@@ -0,0 +1,171 @@
+= Daemons Release History
+
+== Release 1.1.4: June 17, 2011
+
+* Do not change the umask to 0000 when daemonizing anymore, just leave it as it 
+  was (thanks to Jon Botelho).
+
+== Release 1.1.3: April 14, 2011
+
+* Fixed a bug in Application.stop: the cached pid number needs to
+  be used to check for the status of a killed process (thanks to Jimmy Sieben).
+  
+== Release 1.1.2: March 29, 2011
+
+* Fixed gemspec to include all needed files.
+  
+== Release 1.1.1: March 29, 2011
+
+* Make the logging facilities work in :mode => :none (i.e. when calling 
+  Daemons.daemonize) (thanks to the input from Peter Hegedus).
+  
+== Release 1.1.0: June 20, 2010
+
+* Honour the options[:app_name] in Daemons.daemonize (thanks to Ryan Tecco).
+* Included a new option :stop_proc to specify a proc that will be called when a 
+  daemonized process receives a request to stop (thanks to Dave Dupre).
+* Only delete the pidfile if the current pid is the original pid (ghazel).
+* Start when restart but no application running (pcreux).
+* Silently continue if there is no pidfile (ghazel).
+* We now per default wait for processes to stop and
+  kill them automatically it if they do not stop within a given time 
+  (force_kill_waittime). Use the option --no_wait to not wait for processes to 
+  stop.
+* Set log files mode to 0644 (mikehale).
+* Set pid file permissions to 0644 (mikehale).
+* Added ability to change process uid/gid (mikehale).
+* Fix for: If you happen to start a daemon from a process that has open file 
+  descriptors these will stay open. As it is daemonize.rb only closes ruby IO 
+  objects (thanks to Han Holl).
+* New reload command (SIGHUP) (thanks to Michael Schuerig).
+
+== Release 1.0.10: March 21, 2008
+
+* By default, we now delete stray pid-files (i.e. pid-files which result for 
+  example from a killed daemon) automatically. This function can be deactivated 
+  by passing :keep_pid_files => true as an option.
+* All pid files of :multiple daemons new get deleted correctly upon exit of the 
+  daemons (reported by Han Holl).
+* Use the signal 'KILL' instead of 'TERM' on Windows platforms.
+* Use exit! in trap('TERM') instead of exit when option :hard_exit is given 
+  (thanks to Han Holl).
+* Did some clarification on the exception log.
+
+== Release 1.0.9: October 29, 2007
+
+* fixed a severe bug in the new Pid.running? function: function returned true if
+  the process did not exist (thanks to Jeremy Lawler).
+
+== Release 1.0.8: September 24, 2007
+
+* new Pid.running? function. Checking whether a process exists by sending 
+  signal '0' (thanks to Dru Nelson).
+
+== Release 1.0.7: July 7, 2007
+
+* Patch to fix wrong ARGV when using :exec (in def start_exec: 
+  Kernel.exec(script(), *(@app_argv || []))) (thanks to Alex McGuire).
+
+== Release 1.0.6: Mai 8, 2007
+
+* New option to pass an ARGV-style array to run and run_proc (thanks to Marc Evans).
+* Additional patches for '/var/log' (thanks to Marc Evans).
+
+== Release 1.0.5: February 24, 2007
+
+* Applied patch that makes daemons to use '/var/log' as logfile 
+  directory if you use :dir_mode = :system (thanks to Han Holl).
+* Daemons should now work with Ruby 1.9 (at least the basic features).
+
+== Release 1.0.4: January 17, 2007
+
+* Document the :log_output option (thanks to Andrew Kuklewicz).
+* Set STDOUT.sync = true when redirecting to a logfile (thanks to Andrew Kuklewicz).
+* Should now run also correctly when there is no working 'ps ax' on the system 
+  (thanks to Daniel Kehoe).
+
+== Release 1.0.3: November 1, 2006
+
+* Set the app_name correctly also for the monitor process (thanks to Ilya Novoselov).
+
+== Release 1.0.2: September 26, 2006
+
+* Changed the 'ps -ax' call back to 'ps ax'.
+* Fixed the documentation for the :normal :dir_mode.
+* As a default for Daemons.run_proc, the pid file is now saved in the current directory.
+* In :ontop mode for running a proc (this is equal to calling something like 'ruby ctrl_proc.rb run'), 
+  the proc now runs directly in the calling script, not in a forked process anymore (thanks to Paul Butcher).
+* Set $0 to app_name in the daemons (thanks to Ilya Novoselov).
+
+== Release 1.0.1: August 30, 2006
+
+* Fixed a regex for parsing the 'ps ax' system call. (thanks to Garance Alistair Drosehn)
+
+== Release 1.0.0: August 29, 2006
+
+* Fix the parsing of the 'ps ax' system call. (thanks to Garance Alistair Drosehn)
+
+== Release 0.4.4: February 14, 2006
+
+* Several fixes that allow us to use the Daemons::Controller 
+  with a proc instead of wrapping a script file. This gives us all the
+  PID file management, monitoring, command line options, etc. without having
+  to specify a path to our script which can be tricky, especially when using
+  RubyGems. (thanks to John-Mason Shackelford)
+
+== Release 0.4.3: November 29, 2005
+
+* New Option: You can specify the name of the application with :app_name
+  on calling Daemons.run. This will be used to contruct the name of the pid files
+  and log files. Defaults to the basename of the script. (thanks to Stephen R. Veit)
+
+* Bugfix: Handle the case where no controller options are given when calling Daemons, 
+  just options after "--". (thanks to Stephen R. Veit)
+
+
+== Release 0.4.2: November 15, 2005
+
+* Bugfix for problem with :normal pid-file directory mode (pid.rb), fixed (thanks to Stephen R. Veit)
+
+
+== Release 0.4.1: September 11, 2005
+
+* Bugfix for 'run' command line mode: didn't work anymore in 0.4.0, fixed
+
+
+== Release 0.4.0: July 30, 2005
+
+* Two completely new operation modes:
+  1. Call a block as a daemon (<tt>Daemons.call { my_daemon_code }</tt>)
+     and control it from the parent process.
+  2. Daemonize the currently running process (<tt>Daemons.daemonize</tt>)
+  plus the already existing mode to control your scripts (<tt>Daemons.run("script.rb")</tt>)
+* Improved documentation (for example "How does the daemonization process work?")
+* Improved "simulation mode" (<tt>:ontop</tt> option)
+* Some minor bugfixes
+
+
+== Release 0.3.0: April 21, 2005
+
+* New monitor functionality: automatic restarting of your applications if they crash
+* 'restart' command fixed
+* '--force' command modifier (please refer to the documentation)
+* Some more bugfixes and improvements
+
+
+== Release 0.2.1: Mar 21, 2005
+
+* Bugfix for a problem with the 'status' command
+
+
+== Release 0.2.0: Mar 21, 2005
+
+* Exception backtrace functionality added
+* Exec functionality added
+* More examples added
+* New commands: status, zap
+
+
+== Release 0.0.1: Feb 8, 2005
+
+* Initial release
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/TODO b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/TODO
new file mode 100644 (file)
index 0000000..4ca023b
--- /dev/null
@@ -0,0 +1,2 @@
+* put TODOS here
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call.rb
new file mode 100644 (file)
index 0000000..0738cf5
--- /dev/null
@@ -0,0 +1,56 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+
+require 'daemons'
+
+testfile = File.expand_path(__FILE__) + '.log'
+
+
+# On the first call to <tt<call</tt>, an application group (accessible by <tt>Daemons.group</tt>)
+# will be created an the options will be kept within, so you only have to specify
+# <tt>:multiple</tt> once.
+#
+
+options = {
+#  :ontop => true,
+  :multiple => true
+}
+
+
+Daemons.call(options) do
+  File.open(testfile, 'w') {|f|
+    f.puts "test"
+  }
+
+  loop { puts "1"; sleep 5 }
+end
+puts "first task started"
+
+Daemons.call do
+  loop { puts "2"; sleep 4 }
+end
+puts "second task started"
+
+# NOTE: this process will exit after 5 seconds
+Daemons.call do
+  puts "3"
+  sleep 5
+end
+puts "third task started"
+
+puts "waiting 20 seconds..."
+sleep(20)
+
+# This call would result in an exception as it will try to kill the third process 
+# which has already terminated by that time; but using the 'true' parameter forces the
+# stop_all procedure.
+puts "trying to stop all tasks..."
+Daemons.group.stop_all(true)
+
+puts "done"
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call_monitor.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/call/call_monitor.rb
new file mode 100644 (file)
index 0000000..f58430f
--- /dev/null
@@ -0,0 +1,55 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+
+require 'daemons'
+
+testfile = File.expand_path(__FILE__) + '.log'
+
+
+# On the first call to <tt<call</tt>, an application group (accessible by <tt>Daemons.group</tt>)
+# will be created an the options will be kept within, so you only have to specify
+# <tt>:multiple</tt> once.
+#
+
+options = {
+#  :ontop => true,
+  :multiple => true,
+  :monitor => true
+}
+
+
+Daemons.call(options) do
+  loop { puts "1"; sleep 20 }
+end
+puts "first task started"
+
+
+# NOTE: this process will exit after 5 seconds
+Daemons.call do
+  File.open(testfile, 'a') {|f|
+    f.puts "started..."
+    puts "2"
+  
+    sleep 5
+
+    f.puts "...exit"
+  }
+end
+puts "second task started"
+
+puts "waiting 100 seconds..."
+sleep(100)
+
+# This call would result in an exception as it will try to kill the third process 
+# which has already terminated by that time; but using the 'true' parameter forces the
+# stop_all procedure.
+puts "trying to stop all tasks..."
+Daemons.group.stop_all(true)
+
+puts "done"
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/daemonize/daemonize.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/daemonize/daemonize.rb
new file mode 100644 (file)
index 0000000..8dc0ed8
--- /dev/null
@@ -0,0 +1,27 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+
+
+require 'daemons'
+
+
+options = {
+  :log_output => true
+}
+
+
+testfile = File.expand_path(__FILE__) + '.txt'
+
+Daemons.daemonize(options)
+
+puts "some output..."
+
+File.open(testfile, 'w') {|f|
+  f.write("test")
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_crash.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_crash.rb
new file mode 100644 (file)
index 0000000..97d024a
--- /dev/null
@@ -0,0 +1,17 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :log_output => true,
+  :backtrace => true
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver_crashing.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exec.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exec.rb
new file mode 100644 (file)
index 0000000..ccc8f72
--- /dev/null
@@ -0,0 +1,16 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :mode => :exec
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exit.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_exit.rb
new file mode 100644 (file)
index 0000000..9983a03
--- /dev/null
@@ -0,0 +1,15 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver_exiting.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_hanging.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_hanging.rb
new file mode 100644 (file)
index 0000000..f5cb66d
--- /dev/null
@@ -0,0 +1,19 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+options = {
+  #:mode => :exec,
+  :multiple => true,
+  :no_pidfiles => true,
+  :force_kill_waittime => 5
+  #:force_kill_waittime => -1    # do not wait before killing -9
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver_hanging.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_keep_pid_files.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_keep_pid_files.rb
new file mode 100644 (file)
index 0000000..fd3c9c5
--- /dev/null
@@ -0,0 +1,17 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :keep_pid_files => true
+}
+
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options)
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_monitor.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_monitor.rb
new file mode 100644 (file)
index 0000000..48c2827
--- /dev/null
@@ -0,0 +1,16 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :monitor => true
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver_crashing.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_multiple.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_multiple.rb
new file mode 100644 (file)
index 0000000..fa8e776
--- /dev/null
@@ -0,0 +1,16 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :multiple => true
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_normal.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_normal.rb
new file mode 100644 (file)
index 0000000..2b6517d
--- /dev/null
@@ -0,0 +1,12 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'))
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_ontop.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_ontop.rb
new file mode 100644 (file)
index 0000000..ca36222
--- /dev/null
@@ -0,0 +1,16 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :ontop => true
+}
+
+Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options)
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_optionparser.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_optionparser.rb
new file mode 100644 (file)
index 0000000..a84c69b
--- /dev/null
@@ -0,0 +1,43 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+require 'optparse'
+require 'logger'
+require 'ostruct'
+
+
+class MyApp < Logger::Application
+  def initialize(args)
+    super(self.class)
+    @options = OpenStruct.new(:daemonize => true)
+    opts = OptionParser.new do |opts|
+      opts.banner = 'Usage: myapp [options]'
+      opts.separator ''
+      opts.on('-N','--no-daemonize',"Don't run as a daemon") do
+        @options.daemonize = false
+      end
+    end
+    @args = opts.parse!(args)
+  end
+  
+  def run
+    Daemons.run_proc('myapp',{:ARGV => @args, :ontop => !@options.daemonize}) do
+      puts "@options.daemonize: #{@options.daemonize}"
+      STDOUT.sync = true
+      loop do
+        print '.'
+        sleep(2)
+      end
+    end
+  end
+end
+
+
+myapp = MyApp.new(ARGV)
+myapp.run
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc.rb
new file mode 100644 (file)
index 0000000..7bd1fac
--- /dev/null
@@ -0,0 +1,25 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+             :multiple   => false,
+             :ontop      => false,
+             :backtrace  => true,
+             :log_output => true,
+             :monitor    => true
+           }
+           
+Daemons.run_proc('ctrl_proc.rb', options) do
+  loop do
+    puts 'ping from proc!'
+    sleep(3)
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_multiple.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_multiple.rb
new file mode 100644 (file)
index 0000000..8c3b0a2
--- /dev/null
@@ -0,0 +1,22 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+
+options = {
+  :log_output => true,
+  :multiple => true, 
+}
+
+
+Daemons.run_proc('ctrl_proc_multiple.rb', options) do
+  puts "hello"
+  sleep(5)
+  puts "done"
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_simple.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/ctrl_proc_simple.rb
new file mode 100644 (file)
index 0000000..a26fe14
--- /dev/null
@@ -0,0 +1,17 @@
+lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
+
+if File.exist?(File.join(lib_dir, 'daemons.rb'))
+  $LOAD_PATH.unshift lib_dir
+else
+  begin; require 'rubygems'; rescue ::Exception; end
+end
+
+require 'daemons'
+
+           
+Daemons.run_proc('ctrl_proc_simple.rb') do
+  loop do
+    puts 'ping from proc!'
+    sleep(3)
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver.rb
new file mode 100644 (file)
index 0000000..12430b2
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+
+# This is myserver.rb, an example server that is to be controlled by daemons
+# and that does nothing really useful at the moment.
+#
+# Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
+
+loop do
+  puts 'ping from myserver.rb!'
+  sleep(3)
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_crashing.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_crashing.rb
new file mode 100644 (file)
index 0000000..f158e7b
--- /dev/null
@@ -0,0 +1,14 @@
+# This is myserver.rb, an example server that is to be controlled by daemons
+# and that does nothing really useful at the moment.
+#
+# Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
+
+loop do
+  puts 'ping from myserver.rb!'
+  puts 'this example server will crash in 3 seconds...'
+  
+  sleep(3)
+  
+  puts 'CRASH!'
+  raise 'CRASH!'
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_exiting.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_exiting.rb
new file mode 100644 (file)
index 0000000..e5c6fe7
--- /dev/null
@@ -0,0 +1,8 @@
+loop do
+  puts 'ping from myserver.rb!'
+  puts 'this example server will exit in 3 seconds...'
+  
+  sleep(3)
+  
+  Process.exit
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_hanging.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/examples/run/myserver_hanging.rb
new file mode 100644 (file)
index 0000000..8848fe2
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+
+
+# This is myserver.rb, an example server that is to be controlled by daemons
+# and that does nothing really useful at the moment.
+#
+# Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
+
+trap('TERM') {
+  puts "received TERM"
+  
+  loop do
+    puts 'hanging!'
+    sleep(3)
+  end
+}
+
+loop do
+  puts 'ping from myserver.rb!'
+  sleep(3)
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons.rb
new file mode 100644 (file)
index 0000000..1eb936f
--- /dev/null
@@ -0,0 +1,308 @@
+require 'optparse'
+require 'optparse/time'
+
+
+require 'daemons/pidfile'  
+require 'daemons/cmdline'
+require 'daemons/exceptions'
+require 'daemons/monitor'
+
+
+require 'daemons/application'
+require 'daemons/application_group'
+require 'daemons/controller'
+
+require 'timeout'
+
+# All functions and classes that Daemons provides reside in this module.
+#
+# Daemons is normally invoked by one of the following four ways:
+#
+# 1.  <tt>Daemons.run(script, options)</tt>:
+#     This is used in wrapper-scripts that are supposed to control other ruby scripts or
+#     external applications. Control is completely passed to the daemons library.
+#     Such wrapper script need to be invoked with command line options like 'start' or 'stop'
+#     to do anything useful.
+#
+# 2.  <tt>Daemons.run_proc(app_name, options) { (...) }</tt>:
+#     This is used in wrapper-scripts that are supposed to control a proc. 
+#     Control is completely passed to the daemons library.
+#     Such wrapper script need to be invoked with command line options like 'start' or 'stop'
+#     to do anything useful.
+#
+# 3.  <tt>Daemons.call(options) { block }</tt>:
+#     Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
+#     after spawning the daemon with the new Application object as a return value.
+#
+# 4.  <tt>Daemons.daemonize(options)</tt>:
+#     Daemonize the currently runnig process, i.e. the calling process will become a daemon.
+#
+# == What does daemons internally do with my daemons?
+# *or*:: why do my daemons crash when they try to open a file?
+# *or*:: why can I not see any output from the daemon on the console (when using for example +puts+)?
+#
+# From a technical aspect of view, daemons does the following when creating a daemon:
+#
+# 1.  Forks a child (and exits the parent process, if needed)
+# 2.  Becomes a session leader (which detaches the program from
+#     the controlling terminal).
+# 3.  Forks another child process and exits first child. This prevents
+#     the potential of acquiring a controlling terminal.
+# 4.  Changes the current working directory to "/".
+# 5.  Clears the file creation mask (sets +umask+ to 0000).
+# 6.  Closes file descriptors (reopens +STDOUT+ and +STDERR+ to point to a logfile if
+#     possible).
+#
+# So what does this mean for your daemons:
+# - the current directory is '/'
+# - you cannot receive any input from the console (for example no +gets+)
+# - you cannot output anything from the daemons with +puts+/+print+ unless a logfile is used
+#
+# == How do PidFiles work? Where are they stored?
+#
+# Also, you are maybe interested in reading the documentation for the class PidFile.
+# There you can find out about how Daemons works internally and how and where the so
+# called <i>PidFiles</i> are stored.
+#
+module Daemons
+
+  VERSION = "1.1.4"
+  
+  require 'daemons/daemonize'
+  
+  
+  # Passes control to Daemons.
+  # This is used in wrapper-scripts that are supposed to control other ruby scripts or
+  # external applications. Control is completely passed to the daemons library.
+  # Such wrapper script should be invoked with command line options like 'start' or 'stop'
+  # to do anything useful.
+  #
+  # +script+::  This is the path to the script that should be run as a daemon.
+  #             Please note that Daemons runs this script with <tt>load <script></tt>.
+  #             Also note that Daemons cannot detect the directory in which the controlling
+  #             script resides, so this has to be either an absolute path or you have to run
+  #             the controlling script from the appropriate directory.
+  #
+  # +options+:: A hash that may contain one or more of the options listed below
+  #
+  # === Options:
+  # <tt>:app_name</tt>::  The name of the application. This will be
+  #                       used to contruct the name of the pid files
+  #                       and log files. Defaults to the basename of
+  #                       the script.
+  # <tt>:ARGV</tt>::      An array of strings containing parameters and switches for Daemons.
+  #                       This includes both parameters for Daemons itself and the controlled scripted.
+  #                       These are assumed to be separated by an array element '--', .e.g.
+  #                       ['start', 'f', '--', 'param1_for_script', 'param2_for_script'].
+  #                       If not given, ARGV (the parameters given to the Ruby process) will be used.
+  # <tt>:dir_mode</tt>::  Either <tt>:script</tt> (the directory for writing the pid files to 
+  #                       given by <tt>:dir</tt> is interpreted relative
+  #                       to the script location given by +script+, the default) or <tt>:normal</tt> (the directory given by 
+  #                       <tt>:dir</tt> is interpreted as a (absolute or relative) path) or <tt>:system</tt> 
+  #                       (<tt>/var/run</tt> is used as the pid file directory)
+  #
+  # <tt>:dir</tt>::       Used in combination with <tt>:dir_mode</tt> (description above)
+  # <tt>:multiple</tt>::  Specifies whether multiple instances of the same script are allowed to run at the
+  #                       same time
+  # <tt>:ontop</tt>::     When given (i.e. set to true), stay on top, i.e. do not daemonize the application 
+  #                       (but the pid-file and other things are written as usual)
+  # <tt>:mode</tt>::      <tt>:load</tt> Load the script with <tt>Kernel.load</tt>;
+  #                       note that :stop_proc only works for the :load (and :proc) mode.
+  #                       <tt>:exec</tt> Execute the script file with <tt>Kernel.exec</tt>
+  # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the 
+  #                       pid-file directory if the application exits due to an uncaught exception
+  # <tt>:monitor</tt>::   Monitor the programs and restart crashed instances
+  # <tt>:log_dir</tt>::   A specific directory to put the log files into (when not given, resort to the default
+  #                       location as derived from the :dir_mode and :dir options
+  # <tt>:log_output</tt>:: When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named '[app_name].output' in the pid-file directory
+  # <tt>:keep_pid_files</tt>:: When given do not delete lingering pid-files (files for which the process is no longer running).
+  # <tt>:hard_exit</tt>:: When given use exit! to end a daemons instead of exit (this will for example
+  #                       not call at_exit handlers).
+  # <tt>:stop_proc</tt>:: A proc that will be called when the daemonized process receives a request to stop (works only for :load and :proc mode)
+  #
+  # -----
+  # 
+  # === Example:
+  #   options = {
+  #     :app_name   => "my_app",
+  #     :ARGV       => ['start', '-f', '--', 'param_for_myscript']
+  #     :dir_mode   => :script,
+  #     :dir        => 'pids',
+  #     :multiple   => true,
+  #     :ontop      => true,
+  #     :mode       => :exec,
+  #     :backtrace  => true,
+  #     :monitor    => true
+  #   }
+  #
+  #   Daemons.run(File.join(File.dirname(__FILE__), 'myscript.rb'), options)
+  #
+  def run(script, options = {})
+    options[:script] = script
+    @controller = Controller.new(options, options[:ARGV] || ARGV)
+    
+    @controller.catch_exceptions {
+      @controller.run
+    }
+    
+    # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
+    @group = @controller.group
+  end
+  module_function :run
+  
+  
+  # Passes control to Daemons.
+  # This function does the same as Daemons.run except that not a script but a proc
+  # will be run as a daemon while this script provides command line options like 'start' or 'stop'
+  # and the whole pid-file management to control the proc.
+  #
+  # +app_name+::  The name of the application. This will be
+  #               used to contruct the name of the pid files
+  #               and log files. Defaults to the basename of
+  #               the script.
+  # 
+  # +options+::   A hash that may contain one or more of the options listed in the documentation for Daemons.run
+  #
+  # A block must be given to this function. The block will be used as the :proc entry in the options hash.
+  #
+  # -----
+  # 
+  # === Example:
+  #
+  #   Daemons.run_proc('myproc.rb') do
+  #     loop do
+  #       accept_connection()
+  #       read_request()
+  #       send_response()
+  #       close_connection()
+  #     end
+  #   end
+  #
+  def run_proc(app_name, options = {}, &block)
+    options[:app_name] = app_name
+    options[:mode] = :proc
+    options[:proc] = block
+    
+    # we do not have a script location so the the :script :dir_mode cannot be used, change it to :normal
+    if [nil, :script].include? options[:dir_mode]
+      options[:dir_mode] = :normal
+      options[:dir] ||= File.expand_path('.')
+    end
+    
+    @controller = Controller.new(options, options[:ARGV] || ARGV)
+    
+    @controller.catch_exceptions {
+      @controller.run
+    }
+    
+    # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
+    @group = @controller.group
+  end
+  module_function :run_proc
+  
+  
+  # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
+  # after spawning the daemon with the new Application object as a return value.
+  #
+  # +options+:: A hash that may contain one or more of the options listed below
+  #
+  # +block+::   The block to call in the daemon.
+  #
+  # === Options:
+  # <tt>:multiple</tt>::  Specifies whether multiple instances of the same script are allowed to run at the
+  #                       same time
+  # <tt>:ontop</tt>::     When given, stay on top, i.e. do not daemonize the application 
+  # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the 
+  #                       pid-file directory if the application exits due to an uncaught exception
+  # -----
+  # 
+  # === Example:
+  #   options = {
+  #     :backtrace  => true,
+  #     :monitor    => true,
+  #     :ontop      => true
+  #   }
+  #
+  #   Daemons.call(options) begin
+  #     # Server loop:
+  #     loop {
+  #       conn = accept_conn()
+  #       serve(conn)
+  #     }
+  #   end
+  #
+  def call(options = {}, &block)
+    unless block_given?
+      raise "Daemons.call: no block given"
+    end
+    
+    options[:proc] = block
+    options[:mode] = :proc
+    
+    @group ||= ApplicationGroup.new('proc', options)
+    
+    new_app = @group.new_application(options)
+    new_app.start
+
+    return new_app
+  end
+  module_function :call
+  
+  
+  # Daemonize the currently runnig process, i.e. the calling process will become a daemon.
+  #
+  # +options+:: A hash that may contain one or more of the options listed below
+  #
+  # === Options:
+  # <tt>:ontop</tt>::     When given, stay on top, i.e. do not daemonize the application 
+  # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the 
+  #                       pid-file directory if the application exits due to an uncaught exception
+  # <tt>:app_name</tt>::  The name of the application. This will be
+  #                       used to contruct the name of the pid files
+  #                       and log files. Defaults to the basename of
+  #                       the script.
+  # <tt>:dir_mode</tt>::  Either <tt>:script</tt> (the directory for writing files to 
+  #                       given by <tt>:dir</tt> is interpreted relative
+  #                       to the script location given by +script+, the default) or <tt>:normal</tt> (the directory given by 
+  #                       <tt>:dir</tt> is interpreted as a (absolute or relative) path) or <tt>:system</tt> 
+  #                       (<tt>/var/run</tt> is used as the file directory)
+  #
+  # <tt>:dir</tt>::       Used in combination with <tt>:dir_mode</tt> (description above)
+  # <tt>:log_dir</tt>::   A specific directory to put the log files into (when not given, resort to the default
+  #                       location as derived from the :dir_mode and :dir options
+  # <tt>:log_output</tt>:: When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named '[app_name].output' in the pid-file directory
+  # -----
+  # 
+  # === Example:
+  #   options = {
+  #     :backtrace  => true,
+  #     :ontop      => true,
+  #     :log_output => true
+  #   }
+  #
+  #   Daemons.daemonize(options)
+  #
+  #   # Server loop:
+  #   loop {
+  #     conn = accept_conn()
+  #     puts "some text which goes to the output logfile"
+  #     serve(conn)
+  #   }
+  #
+  def daemonize(options = {})
+    options[:script] ||= File.basename(__FILE__)
+    
+    @group ||= ApplicationGroup.new(options[:app_name] || options[:script], options)
+    
+    @group.new_application(:mode => :none).start
+  end
+  module_function :daemonize
+  
+  # Return the internal ApplicationGroup instance.
+  def group; @group; end
+  module_function :group
+  
+  # Return the internal Controller instance.
+  def controller; @controller; end
+  module_function :controller
+end 
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application.rb
new file mode 100644 (file)
index 0000000..74fd443
--- /dev/null
@@ -0,0 +1,467 @@
+require 'daemons/pidfile'
+require 'daemons/pidmem'
+require 'daemons/change_privilege'
+
+require 'timeout'
+
+
+module Daemons
+
+  class Application
+  
+    attr_accessor :app_argv
+    attr_accessor :controller_argv
+    
+    # the Pid instance belonging to this application
+    attr_reader :pid
+    
+    # the ApplicationGroup the application belongs to
+    attr_reader :group
+    
+    # my private options
+    attr_reader :options
+    
+    
+    SIGNAL = (RUBY_PLATFORM =~ /win32/ ? 'KILL' : 'TERM')
+    
+    
+    def initialize(group, add_options = {}, pid = nil)
+      @group = group
+      @options = group.options.dup
+      @options.update(add_options)
+      
+      @dir_mode = @dir = @script = nil
+      
+      @force_kill_waittime = @options[:force_kill_waittime] || 20
+      
+      unless @pid = pid
+        if @options[:no_pidfiles]
+          @pid = PidMem.new
+        elsif dir = pidfile_dir
+          @pid = PidFile.new(dir, @group.app_name, @group.multiple)
+        else
+          @pid = PidMem.new
+        end
+      end
+    end
+    
+    def change_privilege
+      user = options[:user]
+      group = options[:group]
+      CurrentProcess.change_privilege(user, group) if user
+    end
+    
+    def script
+      @script || @group.script
+    end
+    
+    def pidfile_dir
+      Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
+    end
+    
+    def logdir
+      logdir = options[:log_dir]
+      unless logdir
+        logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
+      end
+      logdir
+    end
+    
+    def output_logfile
+      (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil
+    end
+    
+    def logfile
+      logdir ? File.join(logdir, @group.app_name + '.log') : nil
+    end
+    
+    # this function is only used to daemonize the currently running process (Daemons.daemonize)
+    def start_none
+      unless options[:ontop]
+        Daemonize.daemonize(output_logfile, @group.app_name)
+      else
+        Daemonize.simulate(output_logfile)
+      end
+      
+      @pid.pid = Process.pid
+      
+      
+      # We need this to remove the pid-file if the applications exits by itself.
+      # Note that <tt>at_text</tt> will only be run if the applications exits by calling 
+      # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
+      # in your application!
+      #
+      at_exit {
+        begin; @pid.cleanup; rescue ::Exception; end
+        
+        # If the option <tt>:backtrace</tt> is used and the application did exit by itself
+        # create a exception log.
+        if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
+          begin; exception_log(); rescue ::Exception; end
+        end
+          
+      }
+      
+      # This part is needed to remove the pid-file if the application is killed by 
+      # daemons or manually by the user.
+      # Note that the applications is not supposed to overwrite the signal handler for
+      # 'TERM'.
+      #
+      trap(SIGNAL) {
+        begin; @pid.cleanup; rescue ::Exception; end
+        $daemons_sigterm = true
+        
+        if options[:hard_exit]
+          exit!
+        else
+          exit
+        end
+      }
+    end
+    
+    def start_exec
+      if options[:backtrace]
+        puts "option :backtrace is not supported with :mode => :exec, ignoring"
+      end
+      
+      unless options[:ontop]
+        Daemonize.daemonize(output_logfile, @group.app_name)
+      else
+        Daemonize.simulate(output_logfile)
+      end
+      
+      # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run')
+      @pid.pid = Process.pid
+      
+      ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')      
+      # haven't tested yet if this is really passed to the exec'd process...
+      
+      started()
+      Kernel.exec(script(), *(@app_argv || []))
+    end
+    
+    def start_load
+      unless options[:ontop]
+        Daemonize.daemonize(output_logfile, @group.app_name)
+      else
+        Daemonize.simulate(output_logfile)
+      end
+      
+      @pid.pid = Process.pid
+      
+      
+      # We need this to remove the pid-file if the applications exits by itself.
+      # Note that <tt>at_exit</tt> will only be run if the applications exits by calling 
+      # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
+      # in your application!
+      #
+      at_exit {
+        begin; @pid.cleanup; rescue ::Exception; end
+        
+        # If the option <tt>:backtrace</tt> is used and the application did exit by itself
+        # create a exception log.
+        if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
+          begin; exception_log(); rescue ::Exception; end
+        end
+          
+      }
+      
+      # This part is needed to remove the pid-file if the application is killed by 
+      # daemons or manually by the user.
+      # Note that the applications is not supposed to overwrite the signal handler for
+      # 'TERM'.
+      #
+      $daemons_stop_proc = options[:stop_proc]
+      trap(SIGNAL) {
+        begin
+        if $daemons_stop_proc
+          $daemons_stop_proc.call
+        end
+        rescue ::Exception
+        end
+        
+        begin; @pid.cleanup; rescue ::Exception; end
+        $daemons_sigterm = true
+        
+        if options[:hard_exit]
+          exit!
+        else
+          exit
+        end
+      }
+      
+      # Now we really start the script...
+      $DAEMONS_ARGV = @controller_argv
+      ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
+      
+      ARGV.clear
+      ARGV.concat @app_argv if @app_argv
+      
+      started()
+      # TODO: begin - rescue - end around this and exception logging
+      load script()
+    end
+    
+    def start_proc
+      return unless p = options[:proc]
+    
+      myproc = proc do 
+        # We need this to remove the pid-file if the applications exits by itself.
+        # Note that <tt>at_text</tt> will only be run if the applications exits by calling 
+        # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
+        # in your application!
+        #
+        at_exit {
+          begin; @pid.cleanup; rescue ::Exception; end
+
+          # If the option <tt>:backtrace</tt> is used and the application did exit by itself
+          # create a exception log.
+          if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
+            begin; exception_log(); rescue ::Exception; end
+          end
+
+        }
+
+        # This part is needed to remove the pid-file if the application is killed by 
+        # daemons or manually by the user.
+        # Note that the applications is not supposed to overwrite the signal handler for
+        # 'TERM'.
+        #
+        $daemons_stop_proc = options[:stop_proc]
+        trap(SIGNAL) {
+          begin
+          if $daemons_stop_proc
+            $daemons_stop_proc.call
+          end
+          rescue ::Exception
+          end
+          
+          begin; @pid.cleanup; rescue ::Exception; end
+          $daemons_sigterm = true
+
+          if options[:hard_exit]
+            exit!
+          else
+            exit
+          end
+        }
+        
+        p.call()
+      end
+      
+      unless options[:ontop]
+        @pid.pid = Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name)
+        
+      else
+        Daemonize.simulate(output_logfile)
+        
+        @pid.pid = Process.pid
+        
+        myproc.call
+        
+# why did we use this??
+#         Thread.new(&options[:proc])
+
+# why did we use the code below??
+        # unless pid = Process.fork
+        #   @pid.pid = pid
+        #   Daemonize.simulate(logfile)
+        #   options[:proc].call
+        #   exit
+        # else
+        #   Process.detach(@pid.pid)
+        # end
+      end
+      
+      started()
+    end
+    
+    
+    def start
+      change_privilege
+      @group.create_monitor(@group.applications[0] || self) unless options[:ontop]  # we don't monitor applications in the foreground
+      
+      case options[:mode]
+        when :none
+          # this is only used to daemonize the currently running process
+          start_none
+        when :exec
+          start_exec
+        when :load
+          start_load
+        when :proc
+          start_proc
+        else
+          start_load
+      end
+    end
+    
+    def started
+      if pid = @pid.pid
+        puts "#{self.group.app_name}: process with pid #{pid} started."
+        STDOUT.flush
+      end
+    end
+    
+    
+#     def run
+#       if @group.controller.options[:exec]
+#         run_via_exec()
+#       else
+#         run_via_load()
+#       end
+#     end
+#      
+#     def run_via_exec
+#       
+#     end
+#     
+#     def run_via_load
+#       
+#     end
+
+       def reload
+      Process.kill('HUP', @pid.pid)
+    rescue
+      # ignore
+    end
+
+    # This is a nice little function for debugging purposes:
+    # In case a multi-threaded ruby script exits due to an uncaught exception
+    # it may be difficult to find out where the exception came from because
+    # one cannot catch exceptions that are thrown in threads other than the main
+    # thread.
+    #
+    # This function searches for all exceptions in memory and outputs them to STDERR
+    # (if it is connected) and to a log file in the pid-file directory.
+    #
+    def exception_log
+      return unless logfile
+      
+      require 'logger'
+      
+      l_file = Logger.new(logfile)
+      
+      # the code below finds the last exception
+      e = nil
+      
+      ObjectSpace.each_object {|o|
+        if ::Exception === o
+          e = o
+        end
+      }
+     
+      l_file.info "*** below you find the most recent exception thrown, this will be likely (but not certainly) the exception that made the application exit abnormally ***"
+      l_file.error e
+      
+      l_file.info "*** below you find all exception objects found in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***"
+      
+      # this code logs every exception found in memory
+      ObjectSpace.each_object {|o|
+        if ::Exception === o
+          l_file.error o
+        end
+      }
+      
+      l_file.close
+    end
+    
+    
+    def stop(no_wait = false)
+      if not running?
+        self.zap
+        return
+      end
+      
+      pid = @pid.pid
+      
+      # Catch errors when trying to kill a process that doesn't
+      # exist. This happens when the process quits and hasn't been
+      # restarted by the monitor yet. By catching the error, we allow the
+      # pid file clean-up to occur.
+      begin
+        Process.kill(SIGNAL, pid)
+      rescue Errno::ESRCH => e
+        puts "#{e} #{pid}"
+        puts "deleting pid-file."
+      end
+      
+      if not no_wait
+        if @force_kill_waittime > 0
+          puts "#{self.group.app_name}: trying to stop process with pid #{pid}..."
+          STDOUT.flush
+          
+          begin
+            Timeout::timeout(@force_kill_waittime) {
+              while Pid.running?(pid)
+                sleep(0.2)
+              end
+            }
+          rescue Timeout::Error
+            puts "#{self.group.app_name}: process with pid #{pid} won't stop, we forcefully kill it..."
+            STDOUT.flush
+            
+            begin
+              Process.kill('KILL', pid)
+            rescue Errno::ESRCH
+            end
+            
+            begin
+              Timeout::timeout(20) {
+                while Pid.running?(pid)
+                  sleep(1)
+                end
+              }
+            rescue Timeout::Error
+              puts "#{self.group.app_name}: unable to forcefully kill process with pid #{pid}."
+              STDOUT.flush
+            end
+          end
+        end
+        
+        
+      end
+      
+      sleep(0.1)
+      unless Pid.running?(pid)
+        # We try to remove the pid-files by ourselves, in case the application
+        # didn't clean it up.
+        begin; @pid.cleanup; rescue ::Exception; end
+        
+        puts "#{self.group.app_name}: process with pid #{pid} successfully stopped."
+        STDOUT.flush
+      end
+      
+    end
+    
+    def zap
+      @pid.cleanup
+    end
+    
+    def zap!
+      begin; @pid.cleanup; rescue ::Exception; end
+    end
+    
+    def show_status
+      running = self.running?
+      
+      puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid.exist?) ? ' [pid ' + @pid.pid.to_s + ']' : ''}#{(@pid.exist? and not running) ? ' (but pid-file exists: ' + @pid.pid.to_s + ')' : ''}"
+    end
+    
+    # This function implements a (probably too simle) method to detect
+    # whether the program with the pid found in the pid-file is still running.
+    # It just searches for the pid in the output of <tt>ps ax</tt>, which
+    # is probably not a good idea in some cases.
+    # Alternatives would be to use a direct access method the unix process control
+    # system.
+    #
+    def running?
+      if @pid.exist?
+        return Pid.running?(@pid.pid)
+      end
+      
+      return false
+    end
+  end
+  
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application_group.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/application_group.rb
new file mode 100644 (file)
index 0000000..918df6b
--- /dev/null
@@ -0,0 +1,194 @@
+
+module Daemons
+  class ApplicationGroup
+  
+    attr_reader :app_name
+    attr_reader :script
+    
+    attr_reader :monitor
+    
+    #attr_reader :controller
+    
+    attr_reader :options
+    
+    attr_reader :applications
+    
+    attr_accessor :controller_argv
+    attr_accessor :app_argv
+    
+    attr_accessor :dir_mode
+    attr_accessor :dir
+    
+    # true if the application is supposed to run in multiple instances
+    attr_reader :multiple
+    
+    
+    def initialize(app_name, options = {})
+      @app_name = app_name
+      @options = options
+      
+      if options[:script]
+        @script = File.expand_path(options[:script])
+      end
+      
+      #@controller = controller
+      @monitor = nil
+      
+      #options = controller.options
+      
+      @multiple = options[:multiple] || false
+      
+      @dir_mode = options[:dir_mode] || :script
+      @dir = options[:dir] || ''
+      
+      @keep_pid_files = options[:keep_pid_files] || false
+      @no_pidfiles = options[:no_pidfiles] || false
+      
+      #@applications = find_applications(pidfile_dir())
+      @applications = []
+    end
+    
+    # Setup the application group.
+    # Currently this functions calls <tt>find_applications</tt> which finds
+    # all running instances of the application and populates the application array.
+    #
+    def setup
+      @applications = find_applications(pidfile_dir())
+    end
+    
+    def pidfile_dir
+      PidFile.dir(@dir_mode, @dir, script)
+    end  
+    
+    def find_applications(dir)
+      if @no_pidfiles
+        return find_applications_by_app_name(app_name)
+      else
+        return find_applications_by_pidfiles(dir)
+      end
+    end
+    
+    # TODO: identifiy the monitor process
+    def find_applications_by_app_name(app_name)
+      pids = []
+      
+      begin
+      x = `ps auxw | grep -v grep | awk '{print $2, $11, $12}' | grep #{app_name}`
+      if x && x.chomp!
+        processes = x.split(/\n/).compact
+        processes = processes.delete_if do |p|
+          pid, name, add = p.split(/\s/)
+          # We want to make sure that the first part of the process name matches
+          # so that app_name matches app_name_22
+          
+          app_name != name[0..(app_name.length - 1)] and not add.include?(app_name)
+        end
+        pids = processes.map {|p| p.split(/\s/)[0].to_i}
+      end
+      rescue ::Exception
+      end
+      
+      return pids.map {|f|
+        app = Application.new(self, {}, PidMem.existing(f))
+        setup_app(app)
+        app
+      }
+    end
+    
+    def find_applications_by_pidfiles(dir)
+      pid_files = PidFile.find_files(dir, app_name, ! @keep_pid_files)
+      
+      #pp pid_files
+      
+      @monitor = Monitor.find(dir, app_name + '_monitor')
+      
+      pid_files.reject! {|f| f =~ /_monitor.pid$/}
+      
+      return pid_files.map {|f|
+        app = Application.new(self, {}, PidFile.existing(f))
+        setup_app(app)
+        app
+      }
+    end
+    
+    def new_application(add_options = {})
+      if @applications.size > 0 and not @multiple
+        if options[:force]
+          @applications.delete_if {|a|
+            unless a.running?
+              a.zap
+              true
+            end
+          }
+        end
+        
+        raise RuntimeException.new('there is already one or more instance(s) of the program running') unless @applications.empty?
+      end
+      
+      app = Application.new(self, add_options)
+      
+      setup_app(app)
+      
+      @applications << app
+      
+      return app
+    end
+    
+    def setup_app(app)
+      app.controller_argv = @controller_argv
+      app.app_argv = @app_argv
+    end
+    private :setup_app
+    
+    def create_monitor(an_app)
+      return if @monitor
+      
+      if options[:monitor]
+        @monitor = Monitor.new(an_app)
+
+        @monitor.start(@applications)
+      end
+    end
+    
+    def start_all
+      @monitor.stop if @monitor
+      @monitor = nil
+      
+      @applications.each {|a| 
+        fork { 
+          a.start 
+        } 
+      }
+    end
+    
+    def stop_all(no_wait = false)
+      @monitor.stop if @monitor
+      
+      threads = []
+      
+      @applications.each {|a| 
+        threads << Thread.new do
+          a.stop(no_wait)
+        end
+      }
+      
+      threads.each {|t| t.join}
+    end
+    
+    def reload_all
+      @applications.each {|a| a.reload}
+    end
+
+    def zap_all
+      @monitor.stop if @monitor
+      
+      @applications.each {|a| a.zap}
+    end
+    
+    def show_status
+      @applications.each {|a| a.show_status}
+    end
+    
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/change_privilege.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/change_privilege.rb
new file mode 100644 (file)
index 0000000..34f02e6
--- /dev/null
@@ -0,0 +1,19 @@
+require 'daemons/etc_extension'
+
+class CurrentProcess
+  def self.change_privilege(user, group=user)
+    puts "Changing process privilege to #{user}:#{group}"
+  
+    uid, gid = Process.euid, Process.egid
+    target_uid = Etc.getpwnam(user).uid
+    target_gid = Etc.getgrnam(group).gid
+
+    if uid != target_uid || gid != target_gid
+      Process.initgroups(user, target_gid)
+      Process::GID.change_privilege(target_gid)
+      Process::UID.change_privilege(target_uid)
+    end
+  rescue Errno::EPERM => e
+    raise "Couldn't change user and group to #{user}:#{group}: #{e}"
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/cmdline.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/cmdline.rb
new file mode 100644 (file)
index 0000000..aee0e79
--- /dev/null
@@ -0,0 +1,121 @@
+
+module Daemons
+
+  class Optparse
+  
+    attr_reader :usage
+
+    def initialize(controller)
+      @controller = controller
+      @options = {}
+      
+      @opts = OptionParser.new do |opts|
+        opts.banner = ""
+        
+#         opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+#           @options[:verbose] = v
+#         end
+        
+        opts.on("-t", "--ontop", "Stay on top (does not daemonize)") do |t|
+          @options[:ontop] = t
+        end
+        
+        opts.on("-f", "--force", "Force operation") do |t|
+          @options[:force] = t
+        end
+        
+        opts.on("-n", "--no_wait", "Do not wait for processes to stop") do |t|
+          @options[:no_wait] = t
+        end
+        
+        #opts.separator ""
+        #opts.separator "Specific options:"
+
+        
+        opts.separator ""
+        opts.separator "Common options:"
+
+        # No argument, shows at tail.  This will print an options summary
+        opts.on_tail("-h", "--help", "Show this message") do
+          #puts opts
+          #@usage = 
+          controller.print_usage()
+          
+          exit
+        end
+
+        # Switch to print the version.
+        opts.on_tail("--version", "Show version") do
+          puts "daemons version #{Daemons::VERSION}"
+          exit
+        end
+      end  
+      
+      begin
+        @usage = @opts.to_s
+      rescue ::Exception # work around a bug in ruby 1.9
+        @usage = <<END
+            -t, --ontop                      Stay on top (does not daemonize)
+            -f, --force                      Force operation
+            -n, --no_wait                    Do not wait for processes to stop
+
+        Common options:
+            -h, --help                       Show this message
+                --version                    Show version
+END
+      end
+    end
+    
+    
+    #
+    # Return a hash describing the options.
+    #
+    def parse(args)
+      # The options specified on the command line will be collected in *options*.
+      # We set default values here.
+      #options = {}
+      
+      
+      ##pp args
+      @opts.parse(args)
+      
+      return @options
+    end
+
+  end
+  
+  
+  class Controller
+  
+    def print_usage
+      puts "Usage: #{@app_name} <command> <options> -- <application options>"
+      puts
+      puts "* where <command> is one of:"
+      puts "  start         start an instance of the application"
+      puts "  stop          stop all instances of the application"
+      puts "  restart       stop all instances and restart them afterwards"
+      puts "  reload        send a SIGHUP to all instances of the application"
+      puts "  run           start the application and stay on top"
+      puts "  zap           set the application to a stopped state"
+      puts "  status        show status (PID) of application instances"
+      puts
+      puts "* and where <options> may contain several of the following:"
+      
+      puts @optparse.usage
+    end
+    
+    def catch_exceptions(&block)
+      begin
+        block.call
+      rescue CmdException, OptionParser::ParseError => e
+        puts "ERROR: #{e.to_s}"
+        puts
+        print_usage()
+      rescue RuntimeException => e
+        puts "ERROR: #{e.to_s}"
+      end
+    end
+    
+  end
+  
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/controller.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/controller.rb
new file mode 100644 (file)
index 0000000..79a10bd
--- /dev/null
@@ -0,0 +1,140 @@
+
+module Daemons
+  class Controller
+    
+    attr_reader :app_name
+    
+    attr_reader :group
+    
+    attr_reader :options
+    
+    
+    COMMANDS = [
+      'start',
+      'stop',
+      'restart',
+      'run',
+      'zap',
+      'reload',
+      'status'
+    ]
+    
+    def initialize(options = {}, argv = [])
+      @options = options
+      @argv = argv
+      
+      # Allow an app_name to be specified. If not specified use the
+      # basename of the script.
+      @app_name = options[:app_name]
+      
+      if options[:script]
+        @script = File.expand_path(options[:script])
+    
+        @app_name ||= File.split(@script)[1]
+      end
+    
+      @app_name ||= 'unknown_application'
+      
+      @command, @controller_part, @app_part = Controller.split_argv(argv)
+    
+      #@options[:dir_mode] ||= :script
+    
+      @optparse = Optparse.new(self)
+    end
+    
+    
+    # This function is used to do a final update of the options passed to the application
+    # before they are really used.
+    #
+    # Note that this function should only update <tt>@options</tt> and no other variables.
+    #
+    def setup_options
+      #@options[:ontop] ||= true
+    end
+    
+    def run
+      @options.update @optparse.parse(@controller_part).delete_if {|k,v| !v}
+      
+      setup_options()
+      
+      #pp @options
+
+      @group = ApplicationGroup.new(@app_name, @options)
+      @group.controller_argv = @controller_part
+      @group.app_argv = @app_part
+      
+      @group.setup
+      
+      case @command
+        when 'start'
+          @group.new_application.start
+        when 'run'
+          @options[:ontop] ||= true
+          @group.new_application.start
+        when 'stop'
+          @group.stop_all(@options[:no_wait])
+        when 'restart'
+          unless @group.applications.empty?
+            @group.stop_all
+            sleep(1)
+            @group.start_all
+          else
+            puts "Warning: no instances running. Starting..."
+            @group.new_application.start
+          end
+        when 'reload'
+          @group.reload_all
+        when 'zap'
+          @group.zap_all
+        when 'status'
+          unless @group.applications.empty?
+            @group.show_status
+          else
+            puts "#{@group.app_name}: no instances running"
+          end
+        when nil
+          raise CmdException.new('no command given')
+          #puts "ERROR: No command given"; puts
+          
+          #print_usage()
+          #raise('usage function not implemented')
+        else
+          raise Error.new("command '#{@command}' not implemented")
+      end
+    end
+    
+    
+    # Split an _argv_ array.
+    # +argv+ is assumed to be in the following format:
+    #   ['command', 'controller option 1', 'controller option 2', ..., '--', 'app option 1', ...]
+    #
+    # <tt>command</tt> must be one of the commands listed in <tt>COMMANDS</tt>
+    #
+    # *Returns*: the command as a string, the controller options as an array, the appliation options
+    # as an array
+    #
+    def Controller.split_argv(argv)
+      argv = argv.dup
+      
+      command = nil
+      controller_part = []
+      app_part = []
+       
+      if COMMANDS.include? argv[0]
+        command = argv.shift
+      end
+      
+      if i = argv.index('--')
+        # Handle the case where no controller options are given, just
+        # options after "--" as well (i == 0)
+        controller_part = (i == 0 ? [] : argv[0..i-1])
+        app_part = argv[i+1..-1]
+      else
+        controller_part = argv[0..-1]
+      end
+       
+      return command, controller_part, app_part
+    end
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/daemonize.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/daemonize.rb
new file mode 100644 (file)
index 0000000..1fdeaba
--- /dev/null
@@ -0,0 +1,268 @@
+#--
+###############################################################################
+# daemonize.rb is a slightly modified version of daemonize.rb was             #
+# from the Daemonize Library written by Travis Whitton                        #
+# for details, read the notice below                                          #
+###############################################################################
+#++
+#
+#
+# =Daemonize Library
+# 
+# February. 4, 2005 Travis Whitton <whitton@atlantic.net>
+# 
+# Daemonize allows you to easily modify any existing Ruby program to run
+# as a daemon. See README.rdoc for more details.
+# 
+# == How to install
+# 1. su to root
+# 2. ruby install.rb
+# build the docs if you want to
+# 3. rdoc --main README.rdoc daemonize.rb README.rdoc
+# 
+# == Copying
+# The Daemonize extension module is copywrited free software by Travis Whitton
+# <whitton@atlantic.net>. You can redistribute it under the terms specified in
+# the COPYING file of the Ruby distribution.
+# 
+# == WARRANTY
+# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+#
+#
+# ----
+#
+# == Purpose
+# 
+# Daemonize is a module derived from Perl's Proc::Daemon module. This module
+# allows you to easily modify any existing Ruby program to run as a daemon.
+# A daemon is a process that runs in the background with no controlling terminal.
+# Generally servers (like FTP and HTTP servers) run as daemon processes.
+# Note, do not make the mistake that a daemon == server. Converting a program
+# to a daemon by hand is a relatively simple process; however, this module will
+# save you the effort of repeatedly looking up the procedure, and it will also
+# insure that your programs are daemonized in the safest and most corrects
+# fashion possible.
+# 
+# == Procedure
+# 
+# The Daemonize module does the following:
+# 
+# Forks a child and exits the parent process.
+# 
+# Becomes a session leader (which detaches the program from
+# the controlling terminal).
+# 
+# Forks another child process and exits first child. This prevents
+# the potential of acquiring a controlling terminal.
+# 
+# Changes the current working directory to "/".
+# 
+# Clears the file creation mask.
+# 
+# Closes file descriptors.
+# 
+# == Example usage
+# 
+# Using the Daemonize module is extremely simple:
+# 
+#     require 'daemonize'
+# 
+#     class TestDaemon
+#       include Daemonize
+# 
+#       def initialize
+#         daemonize()
+#         loop do
+#           # do some work here
+#         end
+#       end
+#     end
+# 
+# == Credits
+# 
+# Daemonize was written by Travis Whitton and is based on Perl's
+# Proc::Daemonize, which was written by Earl Hood. The above documentation
+# is also partially borrowed from the Proc::Daemonize POD documentation.
+
+
+
+module Daemonize
+  VERSION = "0.1.1m"
+
+  # Try to fork if at all possible retrying every 5 sec if the
+  # maximum process limit for the system has been reached
+  def safefork
+    tryagain = true
+
+    while tryagain
+      tryagain = false
+      begin
+        if pid = fork
+          return pid
+        end
+      rescue Errno::EWOULDBLOCK
+        sleep 5
+        tryagain = true
+      end
+    end
+  end
+  module_function :safefork
+  
+  
+  def simulate(logfile_name = nil)
+    # NOTE: STDOUT and STDERR will not be redirected to the logfile, because in :ontop mode, we normally want to see the output
+    
+    Dir.chdir "/"   # Release old working directory
+
+    # Make sure all file descriptors are closed
+    ObjectSpace.each_object(IO) do |io|
+      unless [STDIN, STDOUT, STDERR].include?(io)
+        begin
+          unless io.closed?
+            io.close
+          end
+        rescue ::Exception
+        end
+      end
+    end
+
+    # Free file descriptors and
+    # point them somewhere sensible
+    # STDOUT/STDERR should go to a logfile
+    
+    begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
+  end
+  module_function :simulate
+  
+  
+  def call_as_daemon(block, logfile_name = nil, app_name = nil)
+    rd, wr = IO.pipe
+    
+    if tmppid = safefork
+      # parent
+      wr.close
+      pid = rd.read.to_i
+      rd.close
+      
+      Process.waitpid(tmppid)
+      
+      return pid
+    else
+      # child
+      
+      rd.close
+      
+      # Detach from the controlling terminal
+      unless sess_id = Process.setsid
+        raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
+      end
+  
+      # Prevent the possibility of acquiring a controlling terminal
+      #if oldmode.zero?
+        trap 'SIGHUP', 'IGNORE'
+        exit if pid = safefork
+      #end
+  
+      wr.write Process.pid
+      wr.close
+      
+      $0 = app_name if app_name
+      
+      Dir.chdir "/"   # Release old working directory
+  
+      # Make sure all file descriptors are closed
+      ObjectSpace.each_object(IO) do |io|
+        unless [STDIN, STDOUT, STDERR].include?(io)
+          begin
+            unless io.closed?
+              io.close
+            end
+          rescue ::Exception
+          end
+        end
+      end
+      
+      ios = Array.new(8192){|i| IO.for_fd(i) rescue nil}.compact
+      ios.each do |io|
+        next if io.fileno < 3
+        io.close
+      end
+
+  
+      redirect_io(logfile_name)  
+    
+      block.call
+      
+      exit
+    end
+  end
+  module_function :call_as_daemon
+  
+  
+  # This method causes the current running process to become a daemon
+  def daemonize(logfile_name = nil, app_name = nil)
+    srand # Split rand streams between spawning and daemonized process
+    safefork and exit # Fork and exit from the parent
+
+    # Detach from the controlling terminal
+    unless sess_id = Process.setsid
+      raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
+    end
+
+    # Prevent the possibility of acquiring a controlling terminal
+    #if oldmode.zero?
+      trap 'SIGHUP', 'IGNORE'
+      exit if pid = safefork
+    #end
+
+    $0 = app_name if app_name
+    
+    Dir.chdir "/"   # Release old working directory
+
+    # Make sure all file descriptors are closed
+    ObjectSpace.each_object(IO) do |io|
+      unless [STDIN, STDOUT, STDERR].include?(io)
+        begin
+          unless io.closed?
+            io.close
+          end
+        rescue ::Exception
+        end
+      end
+    end
+
+    redirect_io(logfile_name)
+    
+    #return oldmode ? sess_id : 0   # Return value is mostly irrelevant
+    return sess_id
+  end
+  module_function :daemonize
+  
+  
+  # Free file descriptors and
+  # point them somewhere sensible
+  # STDOUT/STDERR should go to a logfile
+  def redirect_io(logfile_name)
+    begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
+     
+    if logfile_name
+      begin
+        STDOUT.reopen logfile_name, "a" 
+        File.chmod(0644, logfile_name)
+        STDOUT.sync = true
+      rescue ::Exception
+        begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
+      end
+    else
+      begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
+    end
+    
+    begin; STDERR.reopen STDOUT; rescue ::Exception; end
+    STDERR.sync = true
+  end
+  module_function :redirect_io
+  
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/etc_extension.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/etc_extension.rb
new file mode 100644 (file)
index 0000000..37340fc
--- /dev/null
@@ -0,0 +1,12 @@
+require 'etc'
+
+Etc.instance_eval do
+  def groupname(gid)
+    Etc.group {|e| return e.name if gid == e.gid }
+    nil
+  end
+  def username(uid)
+    Etc.passwd {|e| return e.name if uid == e.uid }
+    nil
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/exceptions.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/exceptions.rb
new file mode 100644 (file)
index 0000000..956cb91
--- /dev/null
@@ -0,0 +1,28 @@
+
+module Daemons
+
+  class Exception < ::RuntimeError
+  end
+  
+  class RuntimeException < Exception
+  end
+  
+  class CmdException < Exception
+  end
+  
+  class Error < Exception
+  end
+  
+  class SystemError < Error
+  
+    attr_reader :system_error
+    
+    def initialize(msg, system_error)
+      super(msg)
+      
+      @system_error = system_error
+    end
+    
+  end
+  
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/monitor.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/monitor.rb
new file mode 100644 (file)
index 0000000..a271958
--- /dev/null
@@ -0,0 +1,138 @@
+
+module Daemons
+
+  require 'daemons/daemonize'
+   
+  class Monitor
+    
+    def self.find(dir, app_name)
+      pid = PidFile.find_files(dir, app_name, false)[0]
+      
+      if pid
+        pid = PidFile.existing(pid)
+        
+        unless PidFile.running?(pid.pid)
+          begin; pid.cleanup; rescue ::Exception; end
+          return
+        end
+        
+        monitor = self.allocate
+      
+        monitor.instance_variable_set(:@pid, pid)
+        
+        return monitor
+      end
+      
+      return nil
+    end
+    
+    
+    def initialize(an_app)
+      @app = an_app
+      @app_name = an_app.group.app_name + '_monitor'
+      
+      if an_app.pidfile_dir
+        @pid = PidFile.new(an_app.pidfile_dir, @app_name, false)
+      else
+        @pid = PidMem.new
+      end
+    end
+    
+    def watch(applications)
+      sleep(30)
+      
+      loop do
+        applications.each {|a|
+          sleep(10)
+          
+          unless a.running?
+            a.zap!
+            
+            Process.detach(fork { a.start })
+            
+            sleep(10)
+          end
+        }
+        
+        sleep(30)
+      end
+    end
+    private :watch
+    
+    
+    def start_with_pidfile(applications)
+      fork do
+        Daemonize.daemonize(nil, @app_name)
+        
+        begin  
+          @pid.pid = Process.pid
+          
+  #         at_exit {
+  # begin; @pid.cleanup; rescue ::Exception; end
+  #         }
+          
+          # This part is needed to remove the pid-file if the application is killed by 
+          # daemons or manually by the user.
+          # Note that the applications is not supposed to overwrite the signal handler for
+          # 'TERM'.
+          #
+  #         trap('TERM') {
+  # begin; @pid.cleanup; rescue ::Exception; end
+  #           exit
+  #         }
+          
+          watch(applications)
+        rescue ::Exception => e
+          begin
+            File.open(@app.logfile, 'a') {|f|
+              f.puts Time.now
+              f.puts e
+              f.puts e.backtrace.inspect
+            }
+          ensure 
+            begin; @pid.cleanup; rescue ::Exception; end
+            exit!
+          end
+        end
+      end
+    end
+    private :start_with_pidfile
+    
+    def start_without_pidfile(applications)
+      Thread.new { watch(applications) }
+    end
+    private :start_without_pidfile
+    
+    
+    def start(applications)
+      return if applications.empty?
+      
+      if @pid.kind_of?(PidFile)
+        start_with_pidfile(applications)
+      else
+        start_without_pidfile(applications)
+      end
+    end
+    
+    
+    def stop
+      begin
+        pid = @pid.pid
+        Process.kill(Application::SIGNAL, pid)
+               Timeout::timeout(5) {      
+          while Pid.running?(pid)
+            sleep(0.1)
+          end
+        }
+      rescue ::Exception => e
+        puts "#{e} #{pid}"
+        puts "deleting pid-file."
+      end
+      
+      # We try to remove the pid-files by ourselves, in case the application
+      # didn't clean it up.
+      begin; @pid.cleanup; rescue ::Exception; end
+    end
+    
+  end 
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pid.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pid.rb
new file mode 100644 (file)
index 0000000..d76a5e0
--- /dev/null
@@ -0,0 +1,109 @@
+require 'open3'
+
+
+module Daemons
+
+  class Pid
+  
+    def Pid.running?(pid)
+      return false unless pid
+      
+      # Check if process is in existence
+      # The simplest way to do this is to send signal '0'
+      # (which is a single system call) that doesn't actually
+      # send a signal
+      begin
+        Process.kill(0, pid)
+        return true
+      rescue Errno::ESRCH
+        return false
+      rescue ::Exception   # for example on EPERM (process exists but does not belong to us)
+        return true
+      #rescue Errno::EPERM
+      #  return false
+      end
+    end
+    
+  #  def Pid.running?(pid, additional = nil)
+  #    match_pid = Regexp.new("^\\s*#{pid}\\s")
+  #    got_match = false
+  #
+  #    #ps_all = IO.popen('ps ax') # the correct syntax is without a dash (-) !
+  #    ps_in, ps_out, ps_err = Open3.popen3('ps ax') # the correct syntax is without a dash (-) !
+  #    
+  #    return true unless ps_out.gets
+  #    
+  #    begin
+  #      ps_out.each { |psline|
+  #        next unless psline =~ match_pid
+  #        got_match = true
+  #        got_match = false if additional and psline !~ /#{additional}/
+  #        break
+  #      }
+  #    ensure
+  #      begin; begin; ps_in.close; rescue ::Exception; end; begin; ps_out.close; rescue ::Exception; end; ps_err.close; rescue ::Exception; end
+  #    end
+  #    
+  #    # an alternative would be to use the code below, but I don't know whether this is portable
+  #    # `ps axo pid=`.split.include? pid.to_s
+  #     
+  #    return got_match
+  #  end
+    
+    
+    
+    # Returns the directory that should be used to write the pid file to
+    # depending on the given mode.
+    # 
+    # Some modes may require an additionaly hint, others may determine 
+    # the directory automatically.
+    #
+    # If no valid directory is found, returns nil.
+    #
+    def Pid.dir(dir_mode, dir, script)
+      # nil script parameter is allowed as long as dir_mode is not :script
+      return nil if dir_mode == :script && script.nil?                         
+      
+      case dir_mode
+        when :normal
+          return File.expand_path(dir)
+        when :script
+          return File.expand_path(File.join(File.dirname(script),dir))
+        when :system  
+          return '/var/run'
+        else
+          raise Error.new("pid file mode '#{dir_mode}' not implemented")
+      end
+    end
+    
+    # Initialization method
+    def initialize
+    end
+    
+    
+    # Get method
+    def pid
+    end
+    
+    # Set method
+    def pid=(p)
+    end
+    
+    # Check whether the process is running
+    def running?
+      return Pid.running?(pid())
+    end
+    
+    # Cleanup method
+    def cleanup
+    end
+    
+    # Exist? method
+    def exist?
+      true
+    end
+    
+  end  
+  
+  
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidfile.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidfile.rb
new file mode 100644 (file)
index 0000000..1b9cebe
--- /dev/null
@@ -0,0 +1,116 @@
+require 'daemons/pid'
+
+
+module Daemons
+
+  # === What is a Pid-File?
+  # A <i>Pid-File</i> is a file containing the <i>process identification number</i>
+  # (pid) that is stored in a well-defined location of the filesystem thus allowing other
+  # programs to find out the pid of a running script.
+  #
+  # Daemons needs the pid of the scripts that are currently running in the background
+  # to send them so called _signals_. Daemons uses the +TERM+ signal to tell the script
+  # to exit when you issue a +stop+ command.
+  #
+  # === How does a Pid-File look like?
+  #
+  # Pid-Files generated by Daemons have to following format:
+  #   <scriptname>.rb<number>.pid
+  # (Note that <tt><number></tt> is omitted if only one instance of the script can
+  # run at any time)
+  #
+  # Each file just contains one line with the pid as string (for example <tt>6432</tt>).
+  #  
+  # === Where are the Pid-Files stored?
+  # 
+  # Daemons is configurable to store the Pid-Files relative to three different locations:
+  # 1.  in a directory relative to the directory where the script (the one that is supposed to run
+  #     as a daemon) resides (<tt>:script</tt> option for <tt>:dir_mode</tt>)
+  # 2.  in a directory given by <tt>:dir</tt> (<tt>:normal</tt> option for <tt>:dir_mode</tt>)
+  # 3.  in the preconfigured directory <tt>/var/run</tt> (<tt>:system</tt> option for <tt>:dir_mode</tt>)
+  #
+  class PidFile < Pid
+
+    attr_reader :dir, :progname, :multiple, :number
+
+    def PidFile.find_files(dir, progname, delete = false)
+      files = Dir[File.join(dir, "#{progname}*.pid")]
+      
+      files.delete_if {|f| not (File.file?(f) and File.readable?(f))}
+      if delete 
+        files.delete_if do |f|
+          pid = File.open(f) {|h| h.read}.to_i
+          rsl =  ! Pid.running?(pid)
+          if rsl
+            puts "pid-file for killed process #{pid} found (#{f}), deleting."
+            begin; File.unlink(f); rescue ::Exception; end
+          end
+          rsl
+        end
+      end
+    
+      return files
+    end
+    
+    def PidFile.existing(path)
+      new_instance = PidFile.allocate
+      
+      new_instance.instance_variable_set(:@path, path)
+      
+      def new_instance.filename
+        return @path
+      end
+      
+      return new_instance
+    end
+    
+    def initialize(dir, progname, multiple = false)
+      @dir = File.expand_path(dir)
+      @progname = progname
+      @multiple = multiple
+      @number = nil
+      @number = 0 if multiple
+      
+      if multiple
+        while File.exist?(filename) and @number < 1024
+          @number += 1
+        end
+        
+        if @number == 1024
+          raise RuntimeException('cannot run more than 1024 instances of the application')
+        end
+      end
+    end
+    
+    def filename
+      File.join(@dir, "#{@progname}#{ @number or '' }.pid")
+    end
+    
+    def exist?
+      File.exist? filename
+    end
+    
+    def pid=(p)
+      File.open(filename, 'w') {|f|
+        f.chmod(0644)
+        f.puts p   #Process.pid
+      }
+    end
+
+    def cleanup
+      File.delete(filename) if pid == Process.pid
+    end
+
+    def pid
+      begin
+        File.open(filename) {|f|
+          return f.gets.to_i
+        }
+      rescue ::Exception
+        return nil
+      end
+    end
+
+  end
+  
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidmem.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/lib/daemons/pidmem.rb
new file mode 100644 (file)
index 0000000..4ab0b69
--- /dev/null
@@ -0,0 +1,19 @@
+require 'daemons/pid'
+
+
+module Daemons
+
+  class PidMem < Pid
+    attr_accessor :pid
+    
+    def PidMem.existing(numeric_pid)
+      new_instance = PidMem.allocate
+      
+      new_instance.instance_variable_set(:@pid, numeric_pid)
+      
+      return new_instance
+    end
+    
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/setup.rb b/ruby/lib/ruby/gems/1.8/gems/daemons-1.1.4/setup.rb
new file mode 100644 (file)
index 0000000..0807023
--- /dev/null
@@ -0,0 +1,1360 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2004 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
+  module Enumerable
+    alias map collect
+  end
+end
+
+unless File.respond_to?(:read)   # Ruby 1.6
+  def File.read(fname)
+    open(fname) {|f|
+      return f.read
+    }
+  end
+end
+
+def File.binread(fname)
+  open(fname, 'rb') {|f|
+    return f.read
+  }
+end
+
+# for corrupted windows stat(2)
+def File.dir?(path)
+  File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+  raise SetupError, msg
+end
+
+#
+# Config
+#
+
+if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+  ARGV.delete(arg)
+  require arg.split(/=/, 2)[1]
+  $".push 'rbconfig.rb'
+else
+  require 'rbconfig'
+end
+
+def multipackage_install?
+  FileTest.directory?(File.dirname($0) + '/packages')
+end
+
+
+class ConfigItem
+  def initialize(name, template, default, desc)
+    @name = name.freeze
+    @template = template
+    @value = default
+    @default = default.dup.freeze
+    @description = desc
+  end
+
+  attr_reader :name
+  attr_reader :description
+
+  attr_accessor :default
+  alias help_default default
+
+  def help_opt
+    "--#{@name}=#{@template}"
+  end
+
+  def value
+    @value
+  end
+
+  def eval(table)
+    @value.gsub(%r<\$([^/]+)>) { table[$1] }
+  end
+
+  def set(val)
+    @value = check(val)
+  end
+
+  private
+
+  def check(val)
+    setup_rb_error "config: --#{name} requires argument" unless val
+    val
+  end
+end
+
+class BoolItem < ConfigItem
+  def config_type
+    'bool'
+  end
+
+  def help_opt
+    "--#{@name}"
+  end
+
+  private
+
+  def check(val)
+    return 'yes' unless val
+    unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
+      setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+    end
+    (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
+  end
+end
+
+class PathItem < ConfigItem
+  def config_type
+    'path'
+  end
+
+  private
+
+  def check(path)
+    setup_rb_error "config: --#{@name} requires argument"  unless path
+    path[0,1] == '$' ? path : File.expand_path(path)
+  end
+end
+
+class ProgramItem < ConfigItem
+  def config_type
+    'program'
+  end
+end
+
+class SelectItem < ConfigItem
+  def initialize(name, template, default, desc)
+    super
+    @ok = template.split('/')
+  end
+
+  def config_type
+    'select'
+  end
+
+  private
+
+  def check(val)
+    unless @ok.include?(val.strip)
+      setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+    end
+    val.strip
+  end
+end
+
+class PackageSelectionItem < ConfigItem
+  def initialize(name, template, default, help_default, desc)
+    super name, template, default, desc
+    @help_default = help_default
+  end
+
+  attr_reader :help_default
+
+  def config_type
+    'package'
+  end
+
+  private
+
+  def check(val)
+    unless File.dir?("packages/#{val}")
+      setup_rb_error "config: no such package: #{val}"
+    end
+    val
+  end
+end
+
+class ConfigTable_class
+
+  def initialize(items)
+    @items = items
+    @table = {}
+    items.each do |i|
+      @table[i.name] = i
+    end
+    ALIASES.each do |ali, name|
+      @table[ali] = @table[name]
+    end
+  end
+
+  include Enumerable
+
+  def each(&block)
+    @items.each(&block)
+  end
+
+  def key?(name)
+    @table.key?(name)
+  end
+
+  def lookup(name)
+    @table[name] or raise ArgumentError, "no such config item: #{name}"
+  end
+
+  def add(item)
+    @items.push item
+    @table[item.name] = item
+  end
+
+  def remove(name)
+    item = lookup(name)
+    @items.delete_if {|i| i.name == name }
+    @table.delete_if {|name, i| i.name == name }
+    item
+  end
+
+  def new
+    dup()
+  end
+
+  def savefile
+    '.config'
+  end
+
+  def load
+    begin
+      t = dup()
+      File.foreach(savefile()) do |line|
+        k, v = *line.split(/=/, 2)
+        t[k] = v.strip
+      end
+      t
+    rescue Errno::ENOENT
+      setup_rb_error $!.message + "#{File.basename($0)} config first"
+    end
+  end
+
+  def save
+    @items.each {|i| i.value }
+    File.open(savefile(), 'w') {|f|
+      @items.each do |i|
+        f.printf "%s=%s\n", i.name, i.value if i.value
+      end
+    }
+  end
+
+  def [](key)
+    lookup(key).eval(self)
+  end
+
+  def []=(key, val)
+    lookup(key).set val
+  end
+
+end
+
+c = ::Config::CONFIG
+
+rubypath = c['bindir'] + '/' + c['ruby_install_name']
+
+major = c['MAJOR'].to_i
+minor = c['MINOR'].to_i
+teeny = c['TEENY'].to_i
+version = "#{major}.#{minor}"
+
+# ruby ver. >= 1.4.4?
+newpath_p = ((major >= 2) or
+             ((major == 1) and
+              ((minor >= 5) or
+               ((minor == 4) and (teeny >= 4)))))
+
+if c['rubylibdir']
+  # V < 1.6.3
+  _stdruby         = c['rubylibdir']
+  _siteruby        = c['sitedir']
+  _siterubyver     = c['sitelibdir']
+  _siterubyverarch = c['sitearchdir']
+elsif newpath_p
+  # 1.4.4 <= V <= 1.6.3
+  _stdruby         = "$prefix/lib/ruby/#{version}"
+  _siteruby        = c['sitedir']
+  _siterubyver     = "$siteruby/#{version}"
+  _siterubyverarch = "$siterubyver/#{c['arch']}"
+else
+  # V < 1.4.4
+  _stdruby         = "$prefix/lib/ruby/#{version}"
+  _siteruby        = "$prefix/lib/ruby/#{version}/site_ruby"
+  _siterubyver     = _siteruby
+  _siterubyverarch = "$siterubyver/#{c['arch']}"
+end
+libdir = '-* dummy libdir *-'
+stdruby = '-* dummy rubylibdir *-'
+siteruby = '-* dummy site_ruby *-'
+siterubyver = '-* dummy site_ruby version *-'
+parameterize = lambda {|path|
+  path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
+      .sub(/\A#{Regexp.quote(libdir)}/,      '$libdir')\
+      .sub(/\A#{Regexp.quote(stdruby)}/,     '$stdruby')\
+      .sub(/\A#{Regexp.quote(siteruby)}/,    '$siteruby')\
+      .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
+}
+libdir          = parameterize.call(c['libdir'])
+stdruby         = parameterize.call(_stdruby)
+siteruby        = parameterize.call(_siteruby)
+siterubyver     = parameterize.call(_siterubyver)
+siterubyverarch = parameterize.call(_siterubyverarch)
+
+if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+  makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+else
+  makeprog = 'make'
+end
+
+common_conf = [
+  PathItem.new('prefix', 'path', c['prefix'],
+               'path prefix of target environment'),
+  PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+               'the directory for commands'),
+  PathItem.new('libdir', 'path', libdir,
+               'the directory for libraries'),
+  PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+               'the directory for shared data'),
+  PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+               'the directory for man pages'),
+  PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+               'the directory for man pages'),
+  PathItem.new('stdruby', 'path', stdruby,
+               'the directory for standard ruby libraries'),
+  PathItem.new('siteruby', 'path', siteruby,
+      'the directory for version-independent aux ruby libraries'),
+  PathItem.new('siterubyver', 'path', siterubyver,
+               'the directory for aux ruby libraries'),
+  PathItem.new('siterubyverarch', 'path', siterubyverarch,
+               'the directory for aux ruby binaries'),
+  PathItem.new('rbdir', 'path', '$siterubyver',
+               'the directory for ruby scripts'),
+  PathItem.new('sodir', 'path', '$siterubyverarch',
+               'the directory for ruby extentions'),
+  PathItem.new('rubypath', 'path', rubypath,
+               'the path to set to #! line'),
+  ProgramItem.new('rubyprog', 'name', rubypath,
+                  'the ruby program using for installation'),
+  ProgramItem.new('makeprog', 'name', makeprog,
+                  'the make program to compile ruby extentions'),
+  SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+                 'shebang line (#!) editing mode'),
+  BoolItem.new('without-ext', 'yes/no', 'no',
+               'does not compile/install ruby extentions')
+]
+class ConfigTable_class   # open again
+  ALIASES = {
+    'std-ruby'         => 'stdruby',
+    'site-ruby-common' => 'siteruby',     # For backward compatibility
+    'site-ruby'        => 'siterubyver',  # For backward compatibility
+    'bin-dir'          => 'bindir',
+    'bin-dir'          => 'bindir',
+    'rb-dir'           => 'rbdir',
+    'so-dir'           => 'sodir',
+    'data-dir'         => 'datadir',
+    'ruby-path'        => 'rubypath',
+    'ruby-prog'        => 'rubyprog',
+    'ruby'             => 'rubyprog',
+    'make-prog'        => 'makeprog',
+    'make'             => 'makeprog'
+  }
+end
+multipackage_conf = [
+  PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+                           'package names that you want to install'),
+  PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+                           'package names that you do not want to install')
+]
+if multipackage_install?
+  ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
+else
+  ConfigTable = ConfigTable_class.new(common_conf)
+end
+
+
+module MetaConfigAPI
+
+  def eval_file_ifexist(fname)
+    instance_eval File.read(fname), fname, 1 if File.file?(fname)
+  end
+
+  def config_names
+    ConfigTable.map {|i| i.name }
+  end
+
+  def config?(name)
+    ConfigTable.key?(name)
+  end
+
+  def bool_config?(name)
+    ConfigTable.lookup(name).config_type == 'bool'
+  end
+
+  def path_config?(name)
+    ConfigTable.lookup(name).config_type == 'path'
+  end
+
+  def value_config?(name)
+    case ConfigTable.lookup(name).config_type
+    when 'bool', 'path'
+      true
+    else
+      false
+    end
+  end
+
+  def add_config(item)
+    ConfigTable.add item
+  end
+
+  def add_bool_config(name, default, desc)
+    ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+  end
+
+  def add_path_config(name, default, desc)
+    ConfigTable.add PathItem.new(name, 'path', default, desc)
+  end
+
+  def set_config_default(name, default)
+    ConfigTable.lookup(name).default = default
+  end
+
+  def remove_config(name)
+    ConfigTable.remove(name)
+  end
+
+end
+
+
+#
+# File Operations
+#
+
+module FileOperations
+
+  def mkdir_p(dirname, prefix = nil)
+    dirname = prefix + File.expand_path(dirname) if prefix
+    $stderr.puts "mkdir -p #{dirname}" if verbose?
+    return if no_harm?
+
+    # does not check '/'... it's too abnormal case
+    dirs = File.expand_path(dirname).split(%r<(?=/)>)
+    if /\A[a-z]:\z/i =~ dirs[0]
+      disk = dirs.shift
+      dirs[0] = disk + dirs[0]
+    end
+    dirs.each_index do |idx|
+      path = dirs[0..idx].join('')
+      Dir.mkdir path unless File.dir?(path)
+    end
+  end
+
+  def rm_f(fname)
+    $stderr.puts "rm -f #{fname}" if verbose?
+    return if no_harm?
+
+    if File.exist?(fname) or File.symlink?(fname)
+      File.chmod 0777, fname
+      File.unlink fname
+    end
+  end
+
+  def rm_rf(dn)
+    $stderr.puts "rm -rf #{dn}" if verbose?
+    return if no_harm?
+
+    Dir.chdir dn
+    Dir.foreach('.') do |fn|
+      next if fn == '.'
+      next if fn == '..'
+      if File.dir?(fn)
+        verbose_off {
+          rm_rf fn
+        }
+      else
+        verbose_off {
+          rm_f fn
+        }
+      end
+    end
+    Dir.chdir '..'
+    Dir.rmdir dn
+  end
+
+  def move_file(src, dest)
+    File.unlink dest if File.exist?(dest)
+    begin
+      File.rename src, dest
+    rescue
+      File.open(dest, 'wb') {|f| f.write File.binread(src) }
+      File.chmod File.stat(src).mode, dest
+      File.unlink src
+    end
+  end
+
+  def install(from, dest, mode, prefix = nil)
+    $stderr.puts "install #{from} #{dest}" if verbose?
+    return if no_harm?
+
+    realdest = prefix ? prefix + File.expand_path(dest) : dest
+    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+    str = File.binread(from)
+    if diff?(str, realdest)
+      verbose_off {
+        rm_f realdest if File.exist?(realdest)
+      }
+      File.open(realdest, 'wb') {|f|
+        f.write str
+      }
+      File.chmod mode, realdest
+
+      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+        if prefix
+          f.puts realdest.sub(prefix, '')
+        else
+          f.puts realdest
+        end
+      }
+    end
+  end
+
+  def diff?(new_content, path)
+    return true unless File.exist?(path)
+    new_content != File.binread(path)
+  end
+
+  def command(str)
+    $stderr.puts str if verbose?
+    system str or raise RuntimeError, "'system #{str}' failed"
+  end
+
+  def ruby(str)
+    command config('rubyprog') + ' ' + str
+  end
+  
+  def make(task = '')
+    command config('makeprog') + ' ' + task
+  end
+
+  def extdir?(dir)
+    File.exist?(dir + '/MANIFEST')
+  end
+
+  def all_files_in(dirname)
+    Dir.open(dirname) {|d|
+      return d.select {|ent| File.file?("#{dirname}/#{ent}") }
+    }
+  end
+
+  REJECT_DIRS = %w(
+    CVS SCCS RCS CVS.adm .svn
+  )
+
+  def all_dirs_in(dirname)
+    Dir.open(dirname) {|d|
+      return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
+    }
+  end
+
+end
+
+
+#
+# Main Installer
+#
+
+module HookUtils
+
+  def run_hook(name)
+    try_run_hook "#{curr_srcdir()}/#{name}" or
+    try_run_hook "#{curr_srcdir()}/#{name}.rb"
+  end
+
+  def try_run_hook(fname)
+    return false unless File.file?(fname)
+    begin
+      instance_eval File.read(fname), fname, 1
+    rescue
+      setup_rb_error "hook #{fname} failed:\n" + $!.message
+    end
+    true
+  end
+
+end
+
+
+module HookScriptAPI
+
+  def get_config(key)
+    @config[key]
+  end
+
+  alias config get_config
+
+  def set_config(key, val)
+    @config[key] = val
+  end
+
+  #
+  # srcdir/objdir (works only in the package directory)
+  #
+
+  #abstract srcdir_root
+  #abstract objdir_root
+  #abstract relpath
+
+  def curr_srcdir
+    "#{srcdir_root()}/#{relpath()}"
+  end
+
+  def curr_objdir
+    "#{objdir_root()}/#{relpath()}"
+  end
+
+  def srcfile(path)
+    "#{curr_srcdir()}/#{path}"
+  end
+
+  def srcexist?(path)
+    File.exist?(srcfile(path))
+  end
+
+  def srcdirectory?(path)
+    File.dir?(srcfile(path))
+  end
+  
+  def srcfile?(path)
+    File.file? srcfile(path)
+  end
+
+  def srcentries(path = '.')
+    Dir.open("#{curr_srcdir()}/#{path}") {|d|
+      return d.to_a - %w(. ..)
+    }
+  end
+
+  def srcfiles(path = '.')
+    srcentries(path).select {|fname|
+      File.file?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+  def srcdirectories(path = '.')
+    srcentries(path).select {|fname|
+      File.dir?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+end
+
+
+class ToplevelInstaller
+
+  Version   = '3.3.1'
+  Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
+
+  TASKS = [
+    [ 'all',      'do config, setup, then install' ],
+    [ 'config',   'saves your configurations' ],
+    [ 'show',     'shows current configuration' ],
+    [ 'setup',    'compiles ruby extentions and others' ],
+    [ 'install',  'installs files' ],
+    [ 'clean',    "does `make clean' for each extention" ],
+    [ 'distclean',"does `make distclean' for each extention" ]
+  ]
+
+  def ToplevelInstaller.invoke
+    instance().invoke
+  end
+
+  @singleton = nil
+
+  def ToplevelInstaller.instance
+    @singleton ||= new(File.dirname($0))
+    @singleton
+  end
+
+  include MetaConfigAPI
+
+  def initialize(ardir_root)
+    @config = nil
+    @options = { 'verbose' => true }
+    @ardir = File.expand_path(ardir_root)
+  end
+
+  def inspect
+    "#<#{self.class} #{__id__()}>"
+  end
+
+  def invoke
+    run_metaconfigs
+    case task = parsearg_global()
+    when nil, 'all'
+      @config = load_config('config')
+      parsearg_config
+      init_installers
+      exec_config
+      exec_setup
+      exec_install
+    else
+      @config = load_config(task)
+      __send__ "parsearg_#{task}"
+      init_installers
+      __send__ "exec_#{task}"
+    end
+  end
+  
+  def run_metaconfigs
+    eval_file_ifexist "#{@ardir}/metaconfig"
+  end
+
+  def load_config(task)
+    case task
+    when 'config'
+      ConfigTable.new
+    when 'clean', 'distclean'
+      if File.exist?(ConfigTable.savefile)
+      then ConfigTable.load
+      else ConfigTable.new
+      end
+    else
+      ConfigTable.load
+    end
+  end
+
+  def init_installers
+    @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
+  end
+
+  #
+  # Hook Script API bases
+  #
+
+  def srcdir_root
+    @ardir
+  end
+
+  def objdir_root
+    '.'
+  end
+
+  def relpath
+    '.'
+  end
+
+  #
+  # Option Parsing
+  #
+
+  def parsearg_global
+    valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
+
+    while arg = ARGV.shift
+      case arg
+      when /\A\w+\z/
+        setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
+        return arg
+
+      when '-q', '--quiet'
+        @options['verbose'] = false
+
+      when       '--verbose'
+        @options['verbose'] = true
+
+      when '-h', '--help'
+        print_usage $stdout
+        exit 0
+
+      when '-v', '--version'
+        puts "#{File.basename($0)} version #{Version}"
+        exit 0
+      
+      when '--copyright'
+        puts Copyright
+        exit 0
+
+      else
+        setup_rb_error "unknown global option '#{arg}'"
+      end
+    end
+
+    nil
+  end
+
+
+  def parsearg_no_options
+    unless ARGV.empty?
+      setup_rb_error "#{task}:  unknown options: #{ARGV.join ' '}"
+    end
+  end
+
+  alias parsearg_show       parsearg_no_options
+  alias parsearg_setup      parsearg_no_options
+  alias parsearg_clean      parsearg_no_options
+  alias parsearg_distclean  parsearg_no_options
+
+  def parsearg_config
+    re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
+    @options['config-opt'] = []
+
+    while i = ARGV.shift
+      if /\A--?\z/ =~ i
+        @options['config-opt'] = ARGV.dup
+        break
+      end
+      m = re.match(i)  or setup_rb_error "config: unknown option #{i}"
+      name, value = *m.to_a[1,2]
+      @config[name] = value
+    end
+  end
+
+  def parsearg_install
+    @options['no-harm'] = false
+    @options['install-prefix'] = ''
+    while a = ARGV.shift
+      case a
+      when /\A--no-harm\z/
+        @options['no-harm'] = true
+      when /\A--prefix=(.*)\z/
+        path = $1
+        path = File.expand_path(path) unless path[0,1] == '/'
+        @options['install-prefix'] = path
+      else
+        setup_rb_error "install: unknown option #{a}"
+      end
+    end
+  end
+
+  def print_usage(out)
+    out.puts 'Typical Installation Procedure:'
+    out.puts "  $ ruby #{File.basename $0} config"
+    out.puts "  $ ruby #{File.basename $0} setup"
+    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
+    out.puts
+    out.puts 'Detailed Usage:'
+    out.puts "  ruby #{File.basename $0} <global option>"
+    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+    fmt = "  %-24s %s\n"
+    out.puts
+    out.puts 'Global options:'
+    out.printf fmt, '-q,--quiet',   'suppress message outputs'
+    out.printf fmt, '   --verbose', 'output messages verbosely'
+    out.printf fmt, '-h,--help',    'print this message'
+    out.printf fmt, '-v,--version', 'print version and quit'
+    out.printf fmt, '   --copyright',  'print copyright and quit'
+    out.puts
+    out.puts 'Tasks:'
+    TASKS.each do |name, desc|
+      out.printf fmt, name, desc
+    end
+
+    fmt = "  %-24s %s [%s]\n"
+    out.puts
+    out.puts 'Options for CONFIG or ALL:'
+    ConfigTable.each do |item|
+      out.printf fmt, item.help_opt, item.description, item.help_default
+    end
+    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+    out.puts
+    out.puts 'Options for INSTALL:'
+    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+    out.printf fmt, '--prefix=path',  'install path prefix', '$prefix'
+    out.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    @installer.exec_config
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    @installer.exec_setup
+  end
+
+  def exec_install
+    @installer.exec_install
+  end
+
+  def exec_show
+    ConfigTable.each do |i|
+      printf "%-20s %s\n", i.name, i.value
+    end
+  end
+
+  def exec_clean
+    @installer.exec_clean
+  end
+
+  def exec_distclean
+    @installer.exec_distclean
+  end
+
+end
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+  include HookUtils
+  include HookScriptAPI
+  include FileOperations
+
+  def initialize(ardir)
+    super
+    @packages = all_dirs_in("#{@ardir}/packages")
+    raise 'no package exists' if @packages.empty?
+  end
+
+  def run_metaconfigs
+    eval_file_ifexist "#{@ardir}/metaconfig"
+    @packages.each do |name|
+      eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
+    end
+  end
+
+  def init_installers
+    @installers = {}
+    @packages.each do |pack|
+      @installers[pack] = Installer.new(@config, @options,
+                                       "#{@ardir}/packages/#{pack}",
+                                       "packages/#{pack}")
+    end
+
+    with    = extract_selection(config('with'))
+    without = extract_selection(config('without'))
+    @selected = @installers.keys.select {|name|
+                  (with.empty? or with.include?(name)) \
+                      and not without.include?(name)
+                }
+  end
+
+  def extract_selection(list)
+    a = list.split(/,/)
+    a.each do |name|
+      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
+    end
+    a
+  end
+
+  def print_usage(f)
+    super
+    f.puts 'Inluded packages:'
+    f.puts '  ' + @packages.sort.join(' ')
+    f.puts
+  end
+
+  #
+  # multi-package metaconfig API
+  #
+
+  attr_reader :packages
+
+  def declare_packages(list)
+    raise 'package list is empty' if list.empty?
+    list.each do |name|
+      raise "directory packages/#{name} does not exist"\
+              unless File.dir?("#{@ardir}/packages/#{name}")
+    end
+    @packages = list
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    run_hook 'pre-config'
+    each_selected_installers {|inst| inst.exec_config }
+    run_hook 'post-config'
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    run_hook 'pre-setup'
+    each_selected_installers {|inst| inst.exec_setup }
+    run_hook 'post-setup'
+  end
+
+  def exec_install
+    run_hook 'pre-install'
+    each_selected_installers {|inst| inst.exec_install }
+    run_hook 'post-install'
+  end
+
+  def exec_clean
+    rm_f ConfigTable.savefile
+    run_hook 'pre-clean'
+    each_selected_installers {|inst| inst.exec_clean }
+    run_hook 'post-clean'
+  end
+
+  def exec_distclean
+    rm_f ConfigTable.savefile
+    run_hook 'pre-distclean'
+    each_selected_installers {|inst| inst.exec_distclean }
+    run_hook 'post-distclean'
+  end
+
+  #
+  # lib
+  #
+
+  def each_selected_installers
+    Dir.mkdir 'packages' unless File.dir?('packages')
+    @selected.each do |pack|
+      $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
+      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+      Dir.chdir "packages/#{pack}"
+      yield @installers[pack]
+      Dir.chdir '../..'
+    end
+  end
+
+  def verbose?
+    @options['verbose']
+  end
+
+  def no_harm?
+    @options['no-harm']
+  end
+
+end
+
+
+class Installer
+
+  FILETYPES = %w( bin lib ext data )
+
+  include HookScriptAPI
+  include HookUtils
+  include FileOperations
+
+  def initialize(config, opt, srcroot, objroot)
+    @config = config
+    @options = opt
+    @srcdir = File.expand_path(srcroot)
+    @objdir = File.expand_path(objroot)
+    @currdir = '.'
+  end
+
+  def inspect
+    "#<#{self.class} #{File.basename(@srcdir)}>"
+  end
+
+  #
+  # Hook Script API base methods
+  #
+
+  def srcdir_root
+    @srcdir
+  end
+
+  def objdir_root
+    @objdir
+  end
+
+  def relpath
+    @currdir
+  end
+
+  #
+  # configs/options
+  #
+
+  def no_harm?
+    @options['no-harm']
+  end
+
+  def verbose?
+    @options['verbose']
+  end
+
+  def verbose_off
+    begin
+      save, @options['verbose'] = @options['verbose'], false
+      yield
+    ensure
+      @options['verbose'] = save
+    end
+  end
+
+  #
+  # TASK config
+  #
+
+  def exec_config
+    exec_task_traverse 'config'
+  end
+
+  def config_dir_bin(rel)
+  end
+
+  def config_dir_lib(rel)
+  end
+
+  def config_dir_ext(rel)
+    extconf if extdir?(curr_srcdir())
+  end
+
+  def extconf
+    opt = @options['config-opt'].join(' ')
+    command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
+  end
+
+  def config_dir_data(rel)
+  end
+
+  #
+  # TASK setup
+  #
+
+  def exec_setup
+    exec_task_traverse 'setup'
+  end
+
+  def setup_dir_bin(rel)
+    all_files_in(curr_srcdir()).each do |fname|
+      adjust_shebang "#{curr_srcdir()}/#{fname}"
+    end
+  end
+
+  def adjust_shebang(path)
+    return if no_harm?
+    tmpfile = File.basename(path) + '.tmp'
+    begin
+      File.open(path, 'rb') {|r|
+        first = r.gets
+        return unless File.basename(config('rubypath')) == 'ruby'
+        return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
+        $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
+        File.open(tmpfile, 'wb') {|w|
+          w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
+          w.write r.read
+        }
+        move_file tmpfile, File.basename(path)
+      }
+    ensure
+      File.unlink tmpfile if File.exist?(tmpfile)
+    end
+  end
+
+  def setup_dir_lib(rel)
+  end
+
+  def setup_dir_ext(rel)
+    make if extdir?(curr_srcdir())
+  end
+
+  def setup_dir_data(rel)
+  end
+
+  #
+  # TASK install
+  #
+
+  def exec_install
+    rm_f 'InstalledFiles'
+    exec_task_traverse 'install'
+  end
+
+  def install_dir_bin(rel)
+    install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
+  end
+
+  def install_dir_lib(rel)
+    install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
+  end
+
+  def install_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    install_files ruby_extentions('.'),
+                  "#{config('sodir')}/#{File.dirname(rel)}",
+                  0555
+  end
+
+  def install_dir_data(rel)
+    install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
+  end
+
+  def install_files(list, dest, mode)
+    mkdir_p dest, @options['install-prefix']
+    list.each do |fname|
+      install fname, dest, mode, @options['install-prefix']
+    end
+  end
+
+  def ruby_scripts
+    collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
+  end
+  
+  # picked up many entries from cvs-1.11.1/src/ignore.c
+  reject_patterns = %w( 
+    core RCSLOG tags TAGS .make.state
+    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+    *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+    *.org *.in .*
+  )
+  mapping = {
+    '.' => '\.',
+    '$' => '\$',
+    '#' => '\#',
+    '*' => '.*'
+  }
+  REJECT_PATTERNS = Regexp.new('\A(?:' +
+                               reject_patterns.map {|pat|
+                                 pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
+                               }.join('|') +
+                               ')\z')
+
+  def collect_filenames_auto
+    mapdir((existfiles() - hookfiles()).reject {|fname|
+             REJECT_PATTERNS =~ fname
+           })
+  end
+
+  def existfiles
+    all_files_in(curr_srcdir()) | all_files_in('.')
+  end
+
+  def hookfiles
+    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+      %w( config setup install clean ).map {|t| sprintf(fmt, t) }
+    }.flatten
+  end
+
+  def mapdir(filelist)
+    filelist.map {|fname|
+      if File.exist?(fname)   # objdir
+        fname
+      else                    # srcdir
+        File.join(curr_srcdir(), fname)
+      end
+    }
+  end
+
+  def ruby_extentions(dir)
+    Dir.open(dir) {|d|
+      ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
+      if ents.empty?
+        setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+      end
+      return ents
+    }
+  end
+
+  #
+  # TASK clean
+  #
+
+  def exec_clean
+    exec_task_traverse 'clean'
+    rm_f ConfigTable.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  def clean_dir_bin(rel)
+  end
+
+  def clean_dir_lib(rel)
+  end
+
+  def clean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'clean' if File.file?('Makefile')
+  end
+
+  def clean_dir_data(rel)
+  end
+
+  #
+  # TASK distclean
+  #
+
+  def exec_distclean
+    exec_task_traverse 'distclean'
+    rm_f ConfigTable.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  def distclean_dir_bin(rel)
+  end
+
+  def distclean_dir_lib(rel)
+  end
+
+  def distclean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'distclean' if File.file?('Makefile')
+  end
+
+  #
+  # lib
+  #
+
+  def exec_task_traverse(task)
+    run_hook "pre-#{task}"
+    FILETYPES.each do |type|
+      if config('without-ext') == 'yes' and type == 'ext'
+        $stderr.puts 'skipping ext/* by user option' if verbose?
+        next
+      end
+      traverse task, type, "#{task}_dir_#{type}"
+    end
+    run_hook "post-#{task}"
+  end
+
+  def traverse(task, rel, mid)
+    dive_into(rel) {
+      run_hook "pre-#{task}"
+      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+      all_dirs_in(curr_srcdir()).each do |d|
+        traverse task, "#{rel}/#{d}", mid
+      end
+      run_hook "post-#{task}"
+    }
+  end
+
+  def dive_into(rel)
+    return unless File.dir?("#{@srcdir}/#{rel}")
+
+    dir = File.basename(rel)
+    Dir.mkdir dir unless File.dir?(dir)
+    prevdir = Dir.pwd
+    Dir.chdir dir
+    $stderr.puts '---> ' + rel if verbose?
+    @currdir = rel
+    yield
+    Dir.chdir prevdir
+    $stderr.puts '<--- ' + rel if verbose?
+    @currdir = File.dirname(rel)
+  end
+
+end
+
+
+if $0 == __FILE__
+  begin
+    if multipackage_install?
+      ToplevelInstallerMulti.invoke
+    else
+      ToplevelInstaller.invoke
+    end
+  rescue SetupError
+    raise if $DEBUG
+    $stderr.puts $!.message
+    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+    exit 1
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/.gitignore b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/.gitignore
new file mode 100644 (file)
index 0000000..3699f26
--- /dev/null
@@ -0,0 +1,14 @@
+pkg
+rdoc
+Makefile
+
+*.bundle
+*.dll
+*.so
+*.jar
+*.class
+*.o
+*.log
+*.def
+*.pdb
+java/src/.project
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/README b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/README
new file mode 100644 (file)
index 0000000..bc61d26
--- /dev/null
@@ -0,0 +1,82 @@
+= RUBY/EventMachine\r
+\r
+Homepage::  http://rubyeventmachine.com\r
+Rubyforge Page:: http://rubyforge.org/projects/eventmachine\r
+Google Group:: http://groups.google.com/group/eventmachine\r
+Mailing List:: http://rubyforge.org/pipermail/eventmachine-talk\r
+RDoc:: http://eventmachine.rubyforge.org\r
+IRC:: ##eventmachine on irc.freenode.net\r
+Copyright:: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Email:: gmail address: garbagecat10\r
+\r
+EventMachine is copyrighted free software made available under the terms\r
+of either the GPL or Ruby's License. See the file COPYING for full licensing\r
+information.\r
+See EventMachine and EventMachine::Connection for documentation and\r
+usage examples.\r
+\r
+EventMachine implements a fast, single-threaded engine for arbitrary network\r
+communications. It's extremely easy to use in Ruby. EventMachine wraps all\r
+interactions with IP sockets, allowing programs to concentrate on the\r
+implementation of network protocols. It can be used to create both network\r
+servers and clients. To create a server or client, a Ruby program only needs\r
+to specify the IP address and port, and provide a Module that implements the\r
+communications protocol. Implementations of several standard network protocols\r
+are provided with the package, primarily to serve as examples. The real goal\r
+of EventMachine is to enable programs to easily interface with other programs\r
+using TCP/IP, especially if custom protocols are required.\r
+\r
+A Ruby program uses EventMachine by registering the addresses and ports of\r
+network servers and clients, and then entering an event-handling loop.\r
+EventMachine contains glue code in Ruby which will execute callbacks to\r
+user-supplied code for all significant events occurring in the clients\r
+and servers. These events include connection acceptance, startup, data-receipt,\r
+shutdown, and timer events. Arbitrary processing can be performed by user code\r
+during event callbacks, including sending data to one or more remote network\r
+peers, startup and shutdown of network connections, and installation of new\r
+event handlers.\r
+\r
+The EventMachine implements a very familiar model for network programming.\r
+It emphasizes: 1) the maximum possible isolation of user code from network\r
+objects like sockets; 2) maximum performance and scalability; and 3) extreme\r
+ease-of-use for user code. It attempts to provide a higher-level interface\r
+than similar projects which expose a variety of low-level event-handling\r
+and networking objects to Ruby programs.\r
+\r
+The design and implementation of EventMachine grows out of nearly ten years\r
+of experience writing high-performance, high-scaling network server applications.\r
+We have taken particular account of the challenges and lessons described as\r
+the "C10K problem" by Dan Kegel and others.\r
+\r
+EventMachine consists of an extension library written in C++ (which can be\r
+accessed from languages other than Ruby), and a Ruby module which can be dropped\r
+into user programs. On most platforms, EventMachine uses the\r
+<tt>select(2)</tt> system call,\r
+so it will run on a large range of Unix-like systems and on Microsoft\r
+Windows with good performance and scalability. On Linux 2.6 kernels, EventMachine\r
+automatically configures itself to use <tt>epoll(4)</tt> instead of\r
+<tt>select(2),</tt> so scalability on that platform can be significantly\r
+improved.\r
+\r
+Here's a fully-functional echo server written with EventMachine:\r
+\r
+ require 'eventmachine'\r
+\r
+ module EchoServer\r
+   def post_init\r
+     puts "-- someone connected to the echo server!"\r
+   end\r
+\r
+   def receive_data data\r
+     send_data ">>>you sent: #{data}"\r
+     close_connection if data =~ /quit/i\r
+   end\r
+\r
+   def unbind\r
+     puts "-- someone disconnected from the echo server!"\r
+   end\r
+ end\r
+\r
+ EventMachine::run {\r
+   EventMachine::start_server "127.0.0.1", 8081, EchoServer\r
+ }
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/Rakefile b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/Rakefile
new file mode 100644 (file)
index 0000000..c35d7f0
--- /dev/null
@@ -0,0 +1,374 @@
+#!/usr/bin/env rake
+#--
+# Ruby/EventMachine
+#   http://rubyeventmachine.com
+#   Copyright (C) 2006-07 by Francis Cianfrocca
+#
+#   This program is copyrighted free software. You may use it under
+#   the terms of either the GPL or Ruby's License. See the file
+#   COPYING in the EventMachine distribution for full licensing
+#   information.
+#
+# $Id$
+#++
+
+### OLD RAKE: ###
+# # The tasks and external gemspecs we used to generate binary gems are now
+# # obsolete. Use Patrick Hurley's gembuilder to build binary gems for any
+# # desired platform.
+# # To build a binary gem on Win32, ensure that the include and lib paths
+# # both contain the proper references to OPENSSL. Use the static version
+# # of the libraries, not the dynamic, otherwise we expose the user to a
+# # runtime dependency.
+#
+# # To build a binary gem for win32, first build rubyeventmachine.so
+# # using VC6 outside of the build tree (the normal way: ruby extconf.rb,
+# # and then nmake). Then copy rubyeventmachine.so into the lib directory,
+# # and run rake gemwin32.
+#
+
+require 'rubygems'  unless defined?(Gem)
+require 'rake'      unless defined?(Rake)
+
+Package = false # Build zips and tarballs?
+Dir.glob('tasks/*.rake').each { |r| Rake.application.add_import r }
+
+# e.g. rake EVENTMACHINE_LIBRARY=java for forcing java build tasks as defaults!
+$eventmachine_library = :java if RUBY_PLATFORM =~ /java/ || ENV['EVENTMACHINE_LIBRARY'] == 'java'
+$eventmachine_library = :pure_ruby if ENV['EVENTMACHINE_LIBRARY'] == 'pure_ruby'
+
+MAKE = ENV['MAKE'] || if RUBY_PLATFORM =~ /mswin/ # mingw uses make.
+  'nmake'
+else
+  'make'
+end
+
+desc "Build gemspec, then build eventmachine, then run tests."
+task :default => [:build, :test]
+
+desc "Build extension (or EVENTMACHINE_LIBRARY) and place in lib"
+build_task = 'ext:build'
+build_task = 'java:build' if $eventmachine_library == :java
+build_task = :dummy_build if $eventmachine_library == :pure_ruby
+task :build => build_task do |t|
+  Dir.glob('{ext,java/src,ext/fastfilereader}/*.{so,bundle,dll,jar}').each do |f|
+    mv f, "lib"
+  end
+end
+
+task :dummy_build
+
+require 'rake/testtask'
+Rake::TestTask.new(:test) do |t|
+  t.pattern = 'tests/**/test_*.rb'
+  t.warning = true
+end
+
+# Basic clean definition, this is enhanced by imports aswell.
+task :clean do
+  chdir 'ext' do
+    sh "#{MAKE} clean" if test ?e, 'Makefile'
+  end
+  chdir 'ext/fastfilereader' do
+    sh "#{MAKE} clean" if test ?e, 'Makefile'
+  end
+  Dir.glob('**/Makefile').each { |file| rm file }
+  Dir.glob('**/*.{o,so,bundle,class,jar,dll,log}').each { |file| rm file }
+  Dir.glob('ext/**/conftest.dSYM').each{ |file| rm_rf file }
+end
+
+Spec = Gem::Specification.new do |s|
+  s.name              = "eventmachine"
+  s.summary           = "Ruby/EventMachine library"
+  s.platform          = Gem::Platform::RUBY
+
+  s.has_rdoc          = true
+  s.rdoc_options      = %w(--title EventMachine --main README --line-numbers -x lib/em/version -x lib/emva -x lib/evma/ -x lib/pr_eventmachine -x lib/jeventmachine)
+  s.extra_rdoc_files  = Dir['README,docs/*']
+
+  s.files             = `git ls-files`.split("\n")
+
+  s.require_path      = 'lib'
+
+  # TODO / XXX - should we enable this? rubygems fails the install if anything
+  # is broken. What we could do is CI submission, though, and always terminate
+  # with a positive code...
+  # s.test_file         = "tests/testem.rb"
+
+  # XXX Using rake to compile extensions breaks when you have multiple ruby installations
+  # and your path isn't set. We can switch back to this once the Gem.exec patch is merged.
+  # s.extensions        = "Rakefile"
+  s.extensions        = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"]
+
+  s.author            = "Francis Cianfrocca"
+  s.email             = "garbagecat10@gmail.com"
+  s.rubyforge_project = 'eventmachine'
+  s.homepage          = "http://rubyeventmachine.com"
+
+  # Pulled in from readme, as code to pull from readme was not working!
+  # Might be worth removing as no one seems to use gem info anyway.
+  s.description = <<-EOD
+EventMachine implements a fast, single-threaded engine for arbitrary network
+communications. It's extremely easy to use in Ruby. EventMachine wraps all
+interactions with IP sockets, allowing programs to concentrate on the
+implementation of network protocols. It can be used to create both network
+servers and clients. To create a server or client, a Ruby program only needs
+to specify the IP address and port, and provide a Module that implements the
+communications protocol. Implementations of several standard network protocols
+are provided with the package, primarily to serve as examples. The real goal
+of EventMachine is to enable programs to easily interface with other programs
+using TCP/IP, especially if custom protocols are required.
+  EOD
+
+  require 'lib/em/version'
+  s.version = EventMachine::VERSION
+end
+
+if RUBY_PLATFORM =~ /mswin/
+  Spec.platform = 'x86-mswin32-60'
+  Spec.files += %w[ lib/rubyeventmachine.so lib/fastfilereaderext.so ]
+  Spec.extensions = nil
+elsif RUBY_PLATFORM =~ /java/
+  Spec.platform = 'java'
+  Spec.files += %w[ lib/em_reactor.jar ]
+  Spec.extensions = nil
+end
+
+# this is a hack right now, it requires installing msysgit in the global path so it can use tar/curl/etc.
+namespace :win32 do
+  task :check_git do
+    unless `git` =~ /rebase/
+      raise 'git not found, install msys git into the GLOBAL PATH: http://msysgit.googlecode.com/files/Git-1.6.2-preview20090308.exe'
+    end
+  end
+
+  task :check_vc6 do
+    begin
+      raise unless `nmake 2>&1` =~ /Microsoft/
+    rescue
+      raise 'VC6 not found, please run c:\vc\setvc.bat vc6'
+    end
+  end
+
+  task :check_perl do
+    unless `perl --version` =~ /ActiveState/
+      raise 'ActiveState perl required to build OpenSSL: http://downloads.activestate.com/ActivePerl/Windows/5.10/ActivePerl-5.10.0.1004-MSWin32-x86-287188.msi'
+    end
+  end
+
+  task :build_openssl => [:check_git, :check_perl, :check_vc6] do
+    mkdir_p 'build'
+    chdir 'build' do
+      unless File.exists?('openssl-0.9.8j')
+        sh 'curl http://www.openssl.org/source/openssl-0.9.8j.tar.gz > openssl.tar.gz'
+        sh 'tar zxvf openssl.tar.gz' rescue nil # fails because of symlinks
+      end
+
+      mkdir_p 'local'
+      chdir 'openssl-0.9.8j' do
+        sh "perl Configure VC-WIN32 --prefix=\"../local/\""
+        sh 'ms\do_ms.bat'
+        sh 'nmake -f ms\nt.mak install'
+      end
+
+      chdir '../ext' do
+        sh 'git clean -fd .'
+      end
+
+      mv 'local/include/openssl', '../ext/'
+      mv 'local/lib/ssleay32.lib', '../ext/'
+      mv 'local/lib/libeay32.lib', '../ext/'
+    end
+  end
+
+  desc "build binary win32 gem"
+  task :gem => :build_openssl do
+    Rake::Task['build'].invoke
+    Rake::Task['gem'].invoke
+  end
+end
+
+namespace :ext do
+  ext_sources = FileList['ext/*.{h,cpp,rb,c}']
+  ffr_sources = FileList['ext/fastfilereader/*.{h,cpp,rb}']
+  file ext_extconf = 'ext/extconf.rb'
+  file ffr_extconf = 'ext/fastfilereader/extconf.rb'
+  ext_libname = "lib/rubyeventmachine.#{Config::CONFIG['DLEXT']}"
+  ffr_libname = "lib/fastfilereaderext.#{Config::CONFIG['DLEXT']}"
+
+  file ext_libname => ext_sources + ['ext/Makefile'] do
+    chdir('ext') { sh MAKE }
+  end
+  
+  file ffr_libname => ffr_sources + ['ext/fastfilereader/Makefile'] do
+    chdir('ext/fastfilereader') { sh MAKE }
+  end
+
+  desc "Build C++ extension"
+  task :build => [:make]
+
+  task :make => ext_libname
+  task :make => ffr_libname
+
+  file 'ext/Makefile' => ext_extconf do
+    chdir 'ext' do
+      ruby 'extconf.rb'
+    end
+  end
+
+  file 'ext/fastfilereader/Makefile' => ffr_extconf do
+    chdir 'ext/fastfilereader' do
+      ruby 'extconf.rb'
+    end
+  end
+end
+
+namespace :java do
+  # This task creates the JRuby JAR file and leaves it in the lib directory.
+  # This step is required before executing the jgem task.
+  desc "Build java extension"
+  task :build => [:jar] do |t|
+    mv 'java/em_reactor.jar', 'lib/'
+  end
+
+  task :compile do
+    chdir('java') do
+      mkdir_p "build"
+      sh 'javac src/com/rubyeventmachine/*.java -d build'
+    end
+  end
+
+  task :jar => [:compile] do
+    chdir('java/build') do
+      sh "jar -cf ../em_reactor.jar com/rubyeventmachine/*.class"
+    end
+  end
+
+  desc "build a java binary gem"
+  task :gem => :build do
+    Spec.platform = 'java'
+    Spec.files += %w[ lib/em_reactor.jar ]
+    Spec.extensions = nil
+
+    Rake::Task['gem'].invoke
+  end
+end
+
+namespace :osx do
+  desc "Build OSX binary gem"
+  task :gem do
+    Spec.platform = RUBY_PLATFORM.sub(/darwin.+$/, 'darwin')
+    Spec.files += %w[ lib/rubyeventmachine.bundle lib/fastfilereaderext.bundle ]
+    Spec.extensions = nil
+
+    Rake::Task['build'].invoke
+    Rake::Task['gem'].invoke
+  end
+
+  # XXX gcc will still prefer the shared libssl on the system, so we need to hack the extconf
+  # XXX to use the static library to make this actually work
+  task :static_gem => [:build_openssl, :gem]
+
+  task :build_openssl do
+    mkdir_p 'build'
+    chdir 'build' do
+      unless File.exists?('openssl-0.9.8j')
+        sh 'curl http://www.openssl.org/source/openssl-0.9.8j.tar.gz > openssl-0.9.8j.tar.gz'
+        sh 'tar zxvf openssl-0.9.8j.tar.gz'
+      end
+
+      mkdir_p 'local'
+      chdir 'openssl-0.9.8j' do
+        local_dir = File.expand_path(File.join(File.dirname(__FILE__),'build','local'))
+        sh "./config --prefix=#{local_dir}"
+        sh 'make'
+        sh 'make install'
+      end
+
+      chdir '../ext' do
+        sh 'git clean -fd .'
+      end
+
+      mv 'local/include/openssl', '../ext/'
+      mv 'local/lib/libssl.a', '../ext/'
+      mv 'local/lib/libcrypto.a', '../ext/'
+    end
+  end
+end
+
+require 'rake/clean'
+
+rdoc_task_type = begin
+  require 'rdoc/task'
+  RDoc::Task
+rescue LoadError
+  require 'rake/rdoctask'
+  Rake::RDocTask
+end
+df = begin; require 'rdoc/generator/darkfish'; true; rescue LoadError; end
+rdtask = rdoc_task_type.new do |rd|
+  rd.title = Spec.name
+  rd.rdoc_dir = 'rdoc'
+  rd.main = "README"
+  rd.rdoc_files.include("lib/**/*.rb", *Spec.extra_rdoc_files)
+  rd.rdoc_files.exclude(*%w(lib/em/version lib/emva lib/evma/ lib/pr_eventmachine lib/jeventmachine))
+  rd.template = 'darkfish' if df
+end
+Rake::Task[:clean].enhance [:clobber_rdoc]
+
+desc 'Generate and open documentation'
+task :docs => :rdoc do
+  case RUBY_PLATFORM
+  when /darwin/       ; sh 'open rdoc/index.html'
+  when /mswin|mingw/  ; sh 'start rdoc\index.html'
+  else 
+    sh 'firefox rdoc/index.html'
+  end
+end
+
+def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end
+def sudo(cmd)
+  if windows? || (require 'etc'; Etc.getpwuid.uid == 0)
+    sh cmd
+  else
+    sh "sudo #{cmd}"
+  end
+end
+def gem_cmd(action, name, *args)
+  rb = Gem.ruby rescue nil
+  rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']))
+  sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'"
+end
+
+begin
+  require 'rubygems/package_task'
+  Gem::PackageTask
+rescue LoadError
+  require 'rake/gempackagetask'
+  Rake::GemPackageTask
+end.new(Spec) do |pkg|
+  pkg.need_tar, pkg.need_tar_gz, pkg.need_zip = true, true, true if Package
+  pkg.gem_spec = Spec
+end
+
+Rake::Task[:clean].enhance [:clobber_package]
+
+namespace :gem do
+  desc 'Install gem (and sudo if required)'
+  task :install => :package do 
+    gem_cmd(:install, "pkg/#{Spec.name}-#{Spec.version}.gem")
+  end
+
+  desc 'Uninstall gem (and sudo if required)'
+  task :uninstall do
+    gem_cmd(:uninstall, "#{Spec.name}", "-v=#{Spec.version}")
+  end
+  
+  desc "Generate new gemspec"
+  task :spec => :clobber do
+    open("eventmachine.gemspec", 'w') { |f| f.write Spec.to_ruby }
+  end
+end
+
+task :clobber => :clean
+task :test => :build
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/COPYING b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/COPYING
new file mode 100644 (file)
index 0000000..ad50768
--- /dev/null
@@ -0,0 +1,60 @@
+EventMachine is copyrighted free software owned by Francis Cianfrocca\r
+(blackhedd ... gmail.com). The Owner of this software permits you to\r
+redistribute and/or modify the software under either the terms of the GPL\r
+version 2 (see the file GPL), or the conditions below ("Ruby License"):\r
+\r
+  1. You may make and give away verbatim copies of the source form of this\r
+     software without restriction, provided that you retain ALL of the\r
+     original copyright notices and associated disclaimers.\r
+\r
+  2. You may modify your copy of the software in any way, provided that\r
+     you do at least ONE of the following:\r
+\r
+       a) place your modifications in the Public Domain or otherwise\r
+          make them Freely Available, such as by posting said\r
+         modifications to Usenet or an equivalent medium, or by allowing\r
+         the author to include your modifications in the software.\r
+\r
+       b) use the modified software only within your corporation or\r
+          organization.\r
+\r
+       c) give non-standard binaries non-standard names, with\r
+          instructions on where to get the original software distribution.\r
+\r
+       d) make other distribution arrangements with the Owner.\r
+\r
+  3. You may distribute the software in object code or binary form,\r
+     provided that you do at least ONE of the following:\r
+\r
+       a) distribute the binaries and library files of the software,\r
+         together with instructions (in a manual page or equivalent)\r
+         on where to get the original distribution.\r
+\r
+       b) accompany the distribution with the machine-readable source of\r
+         the software.\r
+\r
+       c) give non-standard binaries non-standard names, with\r
+          instructions on where to get the original software distribution.\r
+\r
+       d) make other distribution arrangements with the Owner.\r
+\r
+  4. You may modify and include parts of the software into any other\r
+     software (possibly commercial), provided you comply with the terms in\r
+     Sections 1, 2, and 3 above. But some files in the distribution\r
+     are not written by the Owner, so they may be made available to you\r
+     under different terms.\r
+\r
+     For the list of those files and their copying conditions, see the\r
+     file LEGAL.\r
+\r
+  5. The scripts and library files supplied as input to or produced as \r
+     output from the software do not automatically fall under the\r
+     copyright of the software, but belong to whoever generated them, \r
+     and may be sold commercially, and may be aggregated with this\r
+     software.\r
+\r
+  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR\r
+     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED\r
+     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+     PURPOSE.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/ChangeLog b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/ChangeLog
new file mode 100644 (file)
index 0000000..edbe9f5
--- /dev/null
@@ -0,0 +1,211 @@
+01Oct06: Replaced EventMachine#open_datagram_server with a version that can\r
+  take a Class or a Module, instead of just a Module. Thanks to Tobias\r
+  Gustafsson for pointing out the missing case.\r
+04Oct06: Supported subsecond timer resolutions, per request by Jason Roelofs.\r
+05Oct06: Added EventMachine#set_quantum, which sets the timer resolution.\r
+15Nov06: Added Connection#set_comm_inactivity_timeout.\r
+15Nov06: Checked in a Line-and-Text Protocol Handler.\r
+18Nov06: Checked in a Header-and-Body Protocol Handler.\r
+22Nov06: Changed EventMachine#reconnect: no longer excepts when called on an\r
+         already-connected handler.\r
+28Nov06: Supported a binary-unix gem.\r
+19Dec06: Added EventMachine#set_effective_user.\r
+05Jan07: Upped max outstanding timers to 1000.\r
+15May07: Applied Solaris patches from Brett Eisenberg\r
+22May07: Cleaned up the license text in all the source files.\r
+22May07: Released version 0.7.2\r
+\r
+23May07: Per suggestion from Bill Kelly, fixed a bug with the initialization\r
+       of the network libraries under Windows. The goal is to enable EM to\r
+       be used without Ruby.\r
+28May07: Applied patch from Bill Kelly, refactors the declarations of\r
+       event names to make EM easier to use from C programs without Ruby.\r
+31May07: Added a preliminary implementation of EventMachine#popen.\r
+01Jun07: Added EM, a "pseudo-alias" for EventMachine.\r
+01Jun07: Added EM#next_tick.\r
+01Jun07: Added EM::Connection#get_outbound_data_size\r
+05Jun07: Removed the code which loads a pure-Ruby EM library in case the\r
+       compiled extension is unavailable. Suggested by Moshe Litvin.\r
+06Jun07: Preliminary epoll implementation.\r
+12Jun07: Added an evented popen implementation that, like Ruby's, is\r
+       full-duplex and makes the subprocess PID available to the caller.\r
+06Jul07: Performance-tweaked the callback dispatcher in eventmachine.rb.\r
+10Jul07: Released version 0.8.0.\r
+12Jul07: Applied patches from Tim Pease to fix Solaris build problems.\r
+15Jul07: Created a new provisional source branch, experiments/jruby-1.\r
+       This is a preliminary implementation of the EM reactor in Java,\r
+       suitable for use with JRuby.\r
+17Jul07: Added EventMachine#stop_server, per request from Kirk Haines,\r
+       and associated unit tests.\r
+22Jul07: Added EventMachine#stream_file_data. This is a very fast and scalable\r
+       way of sending data from static files over network connections. It\r
+       has separate implementations for small files and large file, and\r
+       has tunings to minimize memory consumption.\r
+26Jul07: Added some patches by Kirk Haines to improve the behavior of\r
+       EM::Connection#send_file_data_to_connection.\r
+26Jul07: Added a C++ module for directly integrating EM into C++ programs\r
+       with no Ruby dependencies. Needs example code.\r
+29Jul07: Added EventMachine::Protocols::LineText2.\r
+29Jul07: Added EventMachine::Protocols::Stomp.\r
+30Jul07: Added sys/stat.h to project.h to fix compilation bug on Darwin.\r
+13Aug07: Added EventMachine#reactor_running?\r
+15Aug07: Added parameters for EventMachine::Connection:start_tls that can be\r
+       used to specify client-side private keys and certificates.\r
+17Aug07: Added EventMachine#run_block, a sugaring for a common use case.\r
+24Aug07: Added a preliminary keyboard handler. Needs docs and testing on\r
+       windows.\r
+26Aug07: Created EventMachine::Spawnable, an implementation of Erlang-like\r
+       processes.\r
+27Aug07: Silenced some -w warnings, requested by James Edward Gray II.\r
+30Aug07: Added cookies to EM::HttpClient#request.\r
+04Sep07: Added an initial implementation of an evented SMTP client.\r
+04Sep07: Added an initial implementation of an evented SMTP server.\r
+10Sep07: Changed EM#spawn to run spawned blocks in the context of the\r
+       SpawnedProcess object, not of whatever was the active object at the\r
+       time of the spawn.\r
+14Sep07: Heartbeats weren't working with EPOLL. Noticed by Brian Candler.\r
+15Sep07: Added some features, tests and documents to Deferrable.\r
+16Sep07: Added [:content] parameter to EM::Protocols::SmtpClient#send.\r
+16Sep07: Bumped version to 0.9.0 in anticipation of a release.\r
+18Sep07: Released version 0.9.0.\r
+19Sep07: Added #receive_reset to EM::Protocols::SmtpServer.\r
+19Sep07: User overrides of EM::Protocols::SmtpServer#receive_recipient can now\r
+       return a Deferrable. Also fixed bug: SmtpClient now raises a protocol\r
+       error if none of its RCPT TO: commands are accepted by the server.\r
+26Sep07: Fixed missing keyboard support for Windows.\r
+03Oct07: Added a default handler for RuntimeErrors emitted from user-written\r
+       code. Suggested by Brian Candler.\r
+19Oct07: Set the SO_BROADCAST option automatically on all UDP sockets.\r
+10Nov07: Forced integer conversion of send_datagram's port parameter.\r
+Suggested by Matthieu Riou.\r
+12Nov07: Added saslauth.rb, a protocol module to replace the Cyrus SASL\r
+daemons saslauthd and pwcheck.\r
+15Nov07: Fixed bug reported by Mark Zvillius. We were failing to dispatch\r
+       zero-length datagrams under certain conditions.\r
+19Nov07: Added EventMachine#set_max_timers. Requested by Matthieu Riou and\r
+       others.\r
+19Nov07: Fixed bug with EM::Connection#start_tls. Was not working with server\r
+       connections. Reported by Michael S. Fischer.\r
+26Nov07: Supported a hack for EventMachine#popen so it can return an exit\r
+       status from subprocesses. Requested by Michael S. Fischer.\r
+30Nov07: Changed Pipe descriptors so that the child-side of the socketpair is\r
+       NOT set nonblocking. Suggested by Duane Johnson.\r
+05Dec07: Re-enabled the pure-Ruby implementation.\r
+06Dec07: Released Version 0.10.0.\r
+13Dec07: Added EM::DeferrableChildProcess\r
+24Dec07: Added a SASL client for simple password authentication.\r
+27Dec07: Removed the hookable error handler. No one was using it and it significantly\r
+       degraded performance.\r
+30Dec07: Implemented Kqueue support for OSX and BSD.\r
+04Jan08: Fixed bug in epoll ("Bad file descriptor"), patch supplied by Chris\r
+       Heath.\r
+04Jan08: Fixed bug reported by Michael S. Fischer. We were terminating\r
+       SSL connections that sent data before the handshake was complete.\r
+08Jan08: Added an OpenBSD branch for extconf.rb, contributed by Guillaume\r
+       Sellier.\r
+19Jan08: Added EM::Connection::get_sockname per request by Michael Fischer.\r
+19Jan08: Supported IPv6 addresses.\r
+30Apr08: Set the NODELAY option on sockets that we connect to other servers.\r
+       Omission noted by Roger Pack.\r
+14May08: Generated a 0.12 release.\r
+15May08: Supported EM#get_sockname for acceptors (TCP server sockets).\r
+       Requested by Roger Pack.\r
+15May08; Accepted a patch from Dan Aquino that allows the interval of a\r
+       PeriodicTimer to be changed on the fly.\r
+15Jun08: Supported nested calls to EM#run. Many people contributed ideas to\r
+       this, notably raggi and tmm1.\r
+20Jul08: Accepted patch from tmm1 for EM#fork_reactor.\r
+28Jul08: Added a Postgres3 implementation, written by FCianfrocca.\r
+14Aug08: Added a patch by Mike Murphy to support basic auth in the http\r
+client.\r
+28Aug08: Added a patch by tmm1 to fix a longstanding problem with Java\r
+data-sends.\r
+13Sep08: Added LineText2#set_binary_mode, a back-compatibility alias.\r
+13Sep08: Modified the load order of protocol libraries in eventmachine.rb\r
+       to permit a modification of HeaderAndContentProtocol.\r
+13Sep08: Modified HeaderAndContent to use LineText2, which is less buggy\r
+       than LineAndTextProtocol. This change may be reversed if we can fix\r
+       the bugs in buftok.\r
+13Sep08: Improved the password handling in the Postgres protocol handler.\r
+15Sep08: Added attach/detach, contributed by Aman Gupta (tmm1) and Riham Aldakkak,\r
+       to support working with file descriptors not created in the reactor.\r
+16Sep08: Added an optional version string to the HTTP client. This is a hack\r
+       that allows a client to specify a version 1.0 request, which\r
+       keeps the server from sending a chunked response. The right way to\r
+       solve this, of course, is to support chunked responses.\r
+23Sep08: ChangeLog Summary for Merge of branches/raggi\r
+Most notable work and patches by Aman Gupta, Roger Pack, and James Tucker. \r
+Patches / Tickets also submitted by: Jeremy Evans, aanand, darix, mmmurf, \r
+danielaquino, macournoyer.\r
+ - Moved docs into docs/ dir\r
+ - Major refactor of rakefile, added generic rakefile helpers in tasks\r
+ - Added example CPP build rakefile in tasks/cpp.rake\r
+ - Moved rake tests out to tasks/tests.rake\r
+ - Added svn ignores where appropriate\r
+ - Fixed jruby build on older java platforms\r
+ - Gem now builds from Rakefile rather than directly via extconf\r
+ - Gem unified for jruby, C++ and pure ruby.\r
+ - Correction for pure C++ build, removing ruby dependency\r
+ - Fix for CYGWIN builds on ipv6\r
+ - Major refactor for extconf.rb\r
+ - Working mingw builds\r
+ - extconf optionally uses pkg_config over manual configuration\r
+ - extconf builds for 1.9 on any system that has 1.9\r
+ - extconf no longer links pthread explicitly\r
+ - looks for kqueue on all *nix systems\r
+ - better error output on std::runtime_error, now says where it came from\r
+ - Fixed some tests on jruby\r
+ - Added test for general send_data flaw, required for a bugfix in jruby build\r
+ - Added timeout to epoll tests\r
+ - Added fixes for java reactor ruby api\r
+ - Small addition of some docs in httpclient.rb and httpcli2.rb\r
+ - Some refactor and fixes in smtpserver.rb\r
+ - Added parenthesis where possible to avoid excess ruby warnings\r
+ - Refactor of $eventmachine_library logic for accuracy and maintenance, jruby\r
+ - EM::start_server now supports unix sockets\r
+ - EM::connect now supports unix sockets\r
+ - EM::defer @threadqueue now handled more gracefully\r
+ - Added better messages on exceptions raised\r
+ - Fix edge case in timer fires\r
+ - Explicitly require buftok.rb\r
+ - Add protocols to autoload, rather than require them all immediately\r
+ - Fix a bug in pr_eventmachine for outbound_q\r
+ - Refactors to take some of the use of defer out of tests.\r
+ - Fixes in EM.defer under start/stop conditions. Reduced scope of threads.\r
+23Sep08: Added patch from tmm1 to avoid popen errors on exit.\r
+30Sep08: Added File.exists? checks in the args for start_tls, as suggested by\r
+  Brian Lopez (brianmario).\r
+10Nov08: ruby 1.9 compatibility enhancements\r
+28Nov08: Allow for older ruby builds where RARRAY_LEN is not defined\r
+03Dec08: allow passing arguments to popen handlers\r
+13Jan09: SSL support for httpclient2 (David Smalley)\r
+22Jan09: Fixed errors on OSX with the kqueue reactor, fixed errors in the pure\r
+  ruby reactor. Added EM.current_time. Added EM.epoll? and EM.kqueue?\r
+27Jan09: Reactor errors are now raised as ruby RuntimeErrors.\r
+28Jan09: Documentation patch from alloy\r
+29Jan09: (Late sign-off) Use a longer timeout for connect_server (Ilya\r
+  Grigorik)\r
+07Feb09: Fix signal handling issues with threads+epoll\r
+07Feb09: Use rb_thread_schedule in the epoll reactor\r
+07Feb09: Use TRAP_BEG/END and rb_thread_schedule in kqueue reactor\r
+08Feb09: Added fastfilereader from swiftiply\r
+08Feb09: 1.9 fix for rb_trap_immediate\r
+08Feb09: Enable rb_thread_blocking_region for 1.9.0 and 1.9.1\r
+10Feb09: Support win32 builds for fastfilereader\r
+10Feb09: Added a new event to indicate completion of SSL handshake on TCP\r
+  connections\r
+10Feb09: Working get_peer_cert method. Returns the certificate as a Ruby\r
+  String in PEM format. (Jake Douglas)\r
+10Feb09: Added EM.get_max_timers\r
+11Feb09: Fix compile options for sun compiler (Alasdairrr)\r
+11Feb09: get_status returns a Process::Status object\r
+12Feb09: Add EM::Protocols::Memcache with simple get/set functionality\r
+19Feb09: Add catch-all EM.error_handler\r
+20Feb09: Support miniunit (1.9)\r
+20Feb09: Return success on content-length = 0 instead of start waiting forever\r
+  (Ugo Riboni)\r
+25Feb09: Allow next_tick to be used to pre-schedule reactor operations before\r
+  EM.run\r
+26Feb09: Added EM.get_connection_count\r
+01Mar09: Switch back to extconf for compiling gem extensions\r
+01Mar09: fixed a small bug with basic auth (mmmurf)\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/DEFERRABLES b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/DEFERRABLES
new file mode 100644 (file)
index 0000000..adb127b
--- /dev/null
@@ -0,0 +1,133 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use deferrables. For more information, see the separate document LIGHTWEIGHT_CONCURRENCY.\r
+\r
+=== What are Deferrables?\r
+\r
+EventMachine's Deferrable borrows heavily from the "deferred" object in Python's "Twisted" event-handling framework. Here's a minimal example that illustrates Deferrable:\r
+\r
+ require 'eventmachine'\r
\r
+ class MyClass\r
+   include EM::Deferrable\r
\r
+   def print_value x\r
+     puts "MyClass instance received #{x}"\r
+   end\r
+ end\r
\r
+ EM.run {\r
+   df = MyClass.new\r
+   df.callback {|x|\r
+     df.print_value(x)\r
+     EM.stop\r
+   }\r
\r
+   EM::Timer.new(2) {\r
+     df.set_deferred_status :succeeded, 100\r
+   }\r
+ }\r
\r
+\r
+This program will spin for two seconds, print out the string "MyClass instance received 100" and then exit. The Deferrable pattern relies on an unusual metaphor that may be unfamiliar to you, unless you've used Python's Twisted. You may need to read the following material through more than once before you get the idea.\r
+\r
+EventMachine::Deferrable is simply a Ruby Module that you can include in your own classes. (There also is a class named EventMachine::DefaultDeferrable for when you want to create one without including it in code of your own.)\r
+\r
+An object that includes EventMachine::Deferrable is like any other Ruby object: it can be created whenever you want, returned from your functions, or passed as an argument to other functions.\r
+\r
+The Deferrable pattern allows you to specify any number of Ruby code blocks (callbacks or errbacks) that will be executed at some future time when the status of the Deferrable object changes.\r
+\r
+How might that be useful? Well, imagine that you're implementing an HTTP server, but you need to make a call to some other server in order to fulfill a client request.\r
+\r
+When you receive a request from one of your clients, you can create and return a Deferrable object. Some other section of your program can add a callback to the Deferrable that will cause the client's request to be fulfilled. Simultaneously, you initiate an event-driven or threaded client request to some different server. And then your EM program will continue to process other events and service other client requests.\r
+\r
+When your client request to the other server completes some time later, you will call the #set_deferred_status method on the Deferrable object, passing either a success or failure status, and an arbitrary number of parameters (which might include the data you received from the other server).\r
+\r
+At that point, the status of the Deferrable object becomes known, and its callback or errback methods are immediately executed. Callbacks and errbacks are code blocks that are attached to Deferrable objects at any time through the methods #callback and #errback.\r
+\r
+The deep beauty of this pattern is that it decouples the disposition of one operation (such as a client request to an outboard server) from the subsequent operations that depend on that disposition (which may include responding to a different client or any other operation).\r
+\r
+The code which invokes the deferred operation (that will eventually result in a success or failure status together with associated data) is completely separate from the code which depends on that status and data. This achieves one of the primary goals for which threading is typically used in sophisticated applications, with none of the nondeterminacy or debugging difficulties of threads.\r
+\r
+As soon as the deferred status of a Deferrable becomes known by way of a call to #set_deferred_status, the Deferrable will IMMEDIATELY execute all of its callbacks or errbacks in the order in which they were added to the Deferrable.\r
+\r
+Callbacks and errbacks can be added to a Deferrable object at any time, not just when the object is created. They can even be added after the status of the object has been determined! (In this case, they will be executed immediately when they are added.)\r
+\r
+A call to Deferrable#set_deferred_status takes :succeeded or :failed as its first argument. (This determines whether the object will call its callbacks or its errbacks.) #set_deferred_status also takes zero or more additional parameters, that will in turn be passed as parameters to the callbacks or errbacks.\r
+\r
+In general, you can only call #set_deferred_status ONCE on a Deferrable object. A call to #set_deferred_status will not return until all of the associated callbacks or errbacks have been called. If you add callbacks or errbacks AFTER making a call to #set_deferred_status, those additional callbacks or errbacks will execute IMMEDIATELY. Any given callback or errback will be executed AT MOST once.\r
+\r
+It's possible to call #set_deferred_status AGAIN, during the execution a callback or errback. This makes it possible to change the parameters which will be sent to the callbacks or errbacks farther down the chain, enabling some extremely elegant use-cases. You can transform the data returned from a deferred operation in arbitrary ways as needed by subsequent users, without changing any of the code that generated the original data.\r
+\r
+A call to #set_deferred_status will not return until all of the associated callbacks or errbacks have been called. If you add callbacks or errbacks AFTER making a call to #set_deferred_status, those additional callbacks or errbacks will execute IMMEDIATELY.\r
+\r
+Let's look at some more sample code. It turns out that many of the internal protocol implementations in the EventMachine package rely on Deferrable. One of these is EM::Protocols::HttpClient.\r
+\r
+To make an evented HTTP request, use the module function EM::Protocols::HttpClient#request, which returns a Deferrable object. Here's how:\r
+\r
+ require 'eventmachine'\r
\r
+ EM.run {\r
+   df = EM::Protocols::HttpClient.request( :host=>"www.example.com", :request=>"/index.html" )\r
\r
+   df.callback {|response|\r
+     puts "Succeeded: #{response[:content]}"\r
+     EM.stop\r
+   }\r
\r
+   df.errback {|response|\r
+     puts "ERROR: #{response[:status]}"\r
+     EM.stop\r
+   }\r
+ }\r
+\r
+(See the documentation of EventMachine::Protocols::HttpClient for information on the object returned by #request.)\r
+\r
+In this code, we make a call to HttpClient#request, which immediately returns a Deferrable object. In the background, an HTTP client request is being made to www.example.com, although your code will continue to run concurrently.\r
+\r
+At some future point, the HTTP client request will complete, and the code in EM::Protocols::HttpClient will process either a valid HTTP response (including returned content), or an error.\r
+\r
+At that point, EM::Protocols::HttpClient will call EM::Deferrable#set_deferred_status on the Deferrable object that was returned to your program, as the return value from EM::Protocols::HttpClient.request. You don't have to do anything to make this happen. All you have to do is tell the Deferrable what to do in case of either success, failure, or both.\r
+\r
+In our code sample, we set one callback and one errback. The former will be called if the HTTP call succeeds, and the latter if it fails. (For simplicity, we have both of them calling EM#stop to end the program, although real programs would be very unlikely to do this.)\r
+\r
+Setting callbacks and errbacks is optional. They are handlers to defined events in the lifecycle of the Deferrable event. It's not an error if you fail to set either a callback, an errback, or both. But of course your program will then fail to receive those notifications.\r
+\r
+If through some bug it turns out that #set_deferred_status is never called on a Deferrable object, then that object's callbacks or errbacks will NEVER be called. It's also possible to set a timeout on a Deferrable. If the timeout elapses before any other call to #set_deferred_status, the Deferrable object will behave as is you had called set_deferred_status(:failed) on it.\r
+\r
+\r
+Now let's modify the example to illustrate some additional points:\r
+\r
+ require 'eventmachine'\r
\r
+ EM.run {\r
+   df = EM::Protocols::HttpClient.request( :host=>"www.example.com", :request=>"/index.html" )\r
\r
+   df.callback {|response|\r
+     df.set_deferred_status :succeeded, response[:content]\r
+   }\r
\r
+   df.callback {|string|\r
+     puts "Succeeded: #{string}"\r
+     EM.stop\r
+   }\r
\r
+   df.errback {|response|\r
+     puts "ERROR: #{response[:status]}"\r
+     EM.stop\r
+   }\r
+ }\r
+\r
+\r
+Just for the sake of illustration, we've now set two callbacks instead of one. If the deferrable operation (the HTTP client-request) succeeds, then both of the callbacks will be executed in order.\r
+\r
+But notice that we've also made our own call to #set_deferred_status in the first callback. This isn't required, because the HttpClient implementation already made a call to #set_deferred_status. (Otherwise, of course, the callback would not be executing.)\r
+\r
+But we used #set_deferred_status in the first callback in order to change the parameters that will be sent to subsequent callbacks in the chain. In this way, you can construct powerful sequences of layered functionality. If you want, you can even change the status of the Deferrable from :succeeded to :failed, which would abort the chain of callback calls, and invoke the chain of errbacks instead.\r
+\r
+Now of course it's somewhat trivial to define two callbacks in the same method, even with the parameter-changing effect we just described. It would be much more interesting to pass the Deferrable to some other function (for example, a function defined in another module or a different gem), that would in turn add callbacks and/or errbacks of its own. That would illustrate the true power of the Deferrable pattern: to isolate the HTTP client-request from other functions that use the data that it returns without caring where those data came from.\r
+\r
+Remember that you can add a callback or an errback to a Deferrable at any point in time, regardless of whether the status of the deferred operation is known (more precisely, regardless of when #set_deferred_status is called on the object). Even hours or days later.\r
+\r
+When you add a callback or errback to a Deferrable object on which #set_deferred_status has not yet been called, the callback/errback is queued up for future execution, inside the Deferrable object. When you add a callback or errback to a Deferrable on which #set_deferred_status has already been called, the callback/errback will be executed immediately. Your code doesn't have to worry about the ordering, and there are no timing issues, as there would be with a threaded approach.\r
+\r
+For more information on Deferrables and their typical usage patterns, look in the EM unit tests. There are also quite a few sugarings (including EM::Deferrable#future) that make typical Deferrable usages syntactically easier to work with.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/EPOLL b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/EPOLL
new file mode 100644 (file)
index 0000000..e687ecc
--- /dev/null
@@ -0,0 +1,141 @@
+EventMachine now supports epoll, bringing large increases in performance and scalability to Ruby programs.\r
+\r
+Epoll(7) is a alternative mechanism for multiplexed I/O that is available in Linux 2.6 kernels.\r
+It features significantly greater performance than the standard select(2) mechanism, when used in\r
+applications that require very large numbers of open I/O descriptors.\r
+\r
+EventMachine has always used select(2) because its behavior is well standardized and broadly supported.\r
+But select becomes unreasonably slow when a program has a\r
+very large number of file descriptors or sockets. Ruby's version of select hardcodes a limit\r
+of 1024 descriptors per process, but heavily loaded processes will start to show performance\r
+degradation even after only a few hundred descriptors are in use.\r
+\r
+Epoll is an extended version of the poll(2) call, and it solves the problems with select. Programs\r
+based on epoll can easily scale past Ruby's 1024-descriptor limit, potentially to tens of thousands\r
+of connectors, with no significant impact on performance.\r
+\r
+(Another alternative which is very similar to epoll in principle is kqueue, supplied on BSD and its\r
+variants.)\r
+\r
+\r
+\r
+This note shows you how to use epoll in your programs.\r
+\r
+=== Compiling EventMachine to use epoll.\r
+\r
+You don't have to do anything to get epoll support in EventMachine.\r
+When you compile EventMachine on a platform that supports epoll, EM will\r
+automatically generate a Makefile that includes epoll. (At this writing, this will only work\r
+on Linux 2.6 kernels.) If you compile EM on a platform without epoll, then epoll support will\r
+be omitted from the Makefile, and EM will work just as it always has.\r
+\r
+=== Using epoll in your programs.\r
+\r
+First, you need to tell EventMachine to use epoll instead of select (but see below, as this requirement\r
+will be removed in a future EventMachine version). Second, you need to prepare your program to use\r
+more than 1024 descriptors, an operation that generally requires superuser privileges. Third, you will probably\r
+want your process to drop the superuser privileges after you increase your process's descriptor limit.\r
+\r
+=== Using EventMachine#epoll\r
+\r
+Call the method EventMachine#epoll anytime before you call EventMachine#run, and your program will\r
+automatically use epoll, if available. It's safe to call EventMachine#epoll on any platform because\r
+it compiles to a no-op on platforms that don't support epoll.\r
+\r
+  require 'rubygems'\r
+  require 'eventmachine'\r
+\r
+  EM.epoll\r
+  EM.run {\r
+    ...\r
+  }\r
+\r
+\r
+EventMachine#epoll was included in this initial release only to avoid changing the behavior of existing\r
+programs. However, it's expected that a future release of EM will convert EventMachine#epoll to a no-op,\r
+and run epoll by default on platforms that support it.\r
+\r
+=== Using EventMachine#set_descriptor_table_size\r
+\r
+In Linux (as in every Unix-like platform), every process has a internal table that determines the maximum\r
+number of file and socket descriptors you may have open at any given time. The size of this table is\r
+generally fixed at 1024, although it may be increased within certain system-defined hard and soft limits.\r
+\r
+If you want your EventMachine program to support more than 1024 total descriptors, you must use \r
+EventMachine#set_descriptor_table_size, as follows:\r
+\r
+  require 'rubygems'\r
+  require 'eventmachine'\r
+\r
+  new_size = EM.set_descriptor_table_size( 60000 )\r
+  $>.puts "New descriptor-table size is #{new_size}"\r
+\r
+  EM.run {\r
+    ...\r
+  }\r
+\r
+If successful, this example will increase the maximum number of descriptors that epoll can use to 60,000.\r
+Call EventMachine#set_descriptor_table_size without an argument at any time to find out the current\r
+size of the descriptor table.\r
+\r
+Using EventMachine#set_descriptor_table_size ONLY affects the number of descriptors that can be used\r
+by epoll. It has no useful effect on platforms that don't support epoll, and it does NOT increase the\r
+number of descriptors that Ruby's own I/O functions can use.\r
+\r
+#set_descriptor_table_size can fail if your process is not running as superuser, or if you try to set a\r
+table size that exceeds the hard limits imposed by your system. In the latter case, try a smaller number.\r
+\r
+\r
+=== Using EventMachine#set_effective_user\r
+\r
+In general, you must run your program with elevated or superuser privileges if you want to increase\r
+your descriptor-table size beyond 1024 descriptors. This is easy enough to verify. Try running the\r
+sample program given above, that increases the descriptor limit to 60,000. You will probably find that\r
+the table size will not be increased if you don't run your program as root or with elevated privileges.\r
+\r
+But of course network servers, especially long-running ones, should not run with elevated privileges.\r
+You will want to drop superuser privileges as soon as possible after initialization. To do this,\r
+use EventMachine#set_effective_user:\r
+\r
+  require 'rubygems'\r
+  require 'eventmachine'\r
+\r
+  # (Here, program is running as superuser)\r
+\r
+  EM.set_descriptor_table_size( 60000 )\r
+  EM.set_effective_user( "nobody" )\r
+  # (Here, program is running as nobody)\r
+\r
+  EM.run {\r
+    ...\r
+  }\r
+\r
+Of course, you will need to replace "nobody" in the example with the name of an unprivileged user\r
+that is valid on your system. What if you want to drop privileges after opening a server socket\r
+on a privileged (low-numbered) port? Easy, just call #set_effective_user after opening your sockets:\r
+\r
+  require 'rubygems'\r
+  require 'eventmachine'\r
+\r
+  # (Here, program is running as superuser)\r
+\r
+  EM.set_descriptor_table_size( 60000 )\r
+\r
+  EM.run {\r
+    EM.start_server( "0.0.0.0", 80, MyHttpServer )\r
+    EM.start_server( "0.0.0.0", 443, MyEncryptedHttpServer )\r
+\r
+    EM.set_effective_user( "nobody" )\r
+    # (Here, program is running as nobody)\r
+\r
+    ...\r
+  }\r
+\r
+\r
+Because EventMachine#set_effective_user is used to enforce security\r
+requirements, it has no nonfatal errors. If you try to set a nonexistent or invalid effective user,\r
+#set_effective_user will abort your program, rather than continue to run with elevated privileges.\r
+\r
+EventMachine#set_effective_user is a silent no-op on platforms that don't support it, such as Windows.\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/GNU b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/GNU
new file mode 100644 (file)
index 0000000..83b2726
--- /dev/null
@@ -0,0 +1,281 @@
+.\r
+\r
+                   GNU GENERAL PUBLIC LICENSE\r
+                      Version 2, June 1991\r
+\r
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\r
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+                           Preamble\r
+\r
+  The licenses for most software are designed to take away your\r
+ freedom to share and change it.  By contrast, the GNU General Public\r
+ License is intended to guarantee your freedom to share and change free\r
+ software--to make sure the software is free for all its users.  This\r
+ General Public License applies to most of the Free Software\r
+ Foundation's software and to any other program whose authors commit to\r
+ using it.  (Some other Free Software Foundation software is covered by\r
+ the GNU Lesser General Public License instead.)  You can apply it to\r
+ your programs, too.\r
+\r
+  When we speak of free software, we are referring to freedom, not\r
+ price.  Our General Public Licenses are designed to make sure that you\r
+ have the freedom to distribute copies of free software (and charge for\r
+ this service if you wish), that you receive source code or can get it\r
+ if you want it, that you can change the software or use pieces of it\r
+ in new free programs; and that you know you can do these things.\r
+\r
+  To protect your rights, we need to make restrictions that forbid\r
+ anyone to deny you these rights or to ask you to surrender the rights.\r
+ These restrictions translate to certain responsibilities for you if you\r
+ distribute copies of the software, or if you modify it.\r
+\r
+  For example, if you distribute copies of such a program, whether\r
+ gratis or for a fee, you must give the recipients all the rights that\r
+ you have.  You must make sure that they, too, receive or can get the\r
+ source code.  And you must show them these terms so they know their\r
+ rights.\r
+\r
+  We protect your rights with two steps: (1) copyright the software, and\r
+ (2) offer you this license which gives you legal permission to copy,\r
+ distribute and/or modify the software.\r
+\r
+  Also, for each author's protection and ours, we want to make certain\r
+ that everyone understands that there is no warranty for this free\r
+ software.  If the software is modified by someone else and passed on, we\r
+ want its recipients to know that what they have is not the original, so\r
+ that any problems introduced by others will not reflect on the original\r
+ authors' reputations.\r
+\r
+  Finally, any free program is threatened constantly by software\r
+ patents.  We wish to avoid the danger that redistributors of a free\r
+ program will individually obtain patent licenses, in effect making the\r
+ program proprietary.  To prevent this, we have made it clear that any\r
+ patent must be licensed for everyone's free use or not licensed at all.\r
+\r
+  The precise terms and conditions for copying, distribution and\r
+ modification follow.\r
+\r
+                   GNU GENERAL PUBLIC LICENSE\r
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+\r
+  0. This License applies to any program or other work which contains\r
+ a notice placed by the copyright holder saying it may be distributed\r
+ under the terms of this General Public License.  The "Program", below,\r
+ refers to any such program or work, and a "work based on the Program"\r
+ means either the Program or any derivative work under copyright law:\r
+ that is to say, a work containing the Program or a portion of it,\r
+ either verbatim or with modifications and/or translated into another\r
+ language.  (Hereinafter, translation is included without limitation in\r
+ the term "modification".)  Each licensee is addressed as "you".\r
+\r
+ Activities other than copying, distribution and modification are not\r
+ covered by this License; they are outside its scope.  The act of\r
+ running the Program is not restricted, and the output from the Program\r
+ is covered only if its contents constitute a work based on the\r
+ Program (independent of having been made by running the Program).\r
+ Whether that is true depends on what the Program does.\r
+\r
+  1. You may copy and distribute verbatim copies of the Program's\r
+ source code as you receive it, in any medium, provided that you\r
+ conspicuously and appropriately publish on each copy an appropriate\r
+ copyright notice and disclaimer of warranty; keep intact all the\r
+ notices that refer to this License and to the absence of any warranty;\r
+ and give any other recipients of the Program a copy of this License\r
+ along with the Program.\r
+\r
+ You may charge a fee for the physical act of transferring a copy, and\r
+ you may at your option offer warranty protection in exchange for a fee.\r
+\r
+  2. You may modify your copy or copies of the Program or any portion\r
+ of it, thus forming a work based on the Program, and copy and\r
+ distribute such modifications or work under the terms of Section 1\r
+ above, provided that you also meet all of these conditions:\r
+\r
+    a) You must cause the modified files to carry prominent notices\r
+    stating that you changed the files and the date of any change.\r
+\r
+    b) You must cause any work that you distribute or publish, that in\r
+    whole or in part contains or is derived from the Program or any\r
+    part thereof, to be licensed as a whole at no charge to all third\r
+    parties under the terms of this License.\r
+\r
+    c) If the modified program normally reads commands interactively\r
+    when run, you must cause it, when started running for such\r
+    interactive use in the most ordinary way, to print or display an\r
+    announcement including an appropriate copyright notice and a\r
+    notice that there is no warranty (or else, saying that you provide\r
+    a warranty) and that users may redistribute the program under\r
+    these conditions, and telling the user how to view a copy of this\r
+    License.  (Exception: if the Program itself is interactive but\r
+    does not normally print such an announcement, your work based on\r
+    the Program is not required to print an announcement.)\r
+\r
+ These requirements apply to the modified work as a whole.  If\r
+ identifiable sections of that work are not derived from the Program,\r
+ and can be reasonably considered independent and separate works in\r
+ themselves, then this License, and its terms, do not apply to those\r
+ sections when you distribute them as separate works.  But when you\r
+ distribute the same sections as part of a whole which is a work based\r
+ on the Program, the distribution of the whole must be on the terms of\r
+ this License, whose permissions for other licensees extend to the\r
+ entire whole, and thus to each and every part regardless of who wrote it.\r
+\r
+ Thus, it is not the intent of this section to claim rights or contest\r
+ your rights to work written entirely by you; rather, the intent is to\r
+ exercise the right to control the distribution of derivative or\r
+ collective works based on the Program.\r
+\r
+ In addition, mere aggregation of another work not based on the Program\r
+ with the Program (or with a work based on the Program) on a volume of\r
+ a storage or distribution medium does not bring the other work under\r
+ the scope of this License.\r
+\r
+  3. You may copy and distribute the Program (or a work based on it,\r
+ under Section 2) in object code or executable form under the terms of\r
+ Sections 1 and 2 above provided that you also do one of the following:\r
+\r
+    a) Accompany it with the complete corresponding machine-readable\r
+    source code, which must be distributed under the terms of Sections\r
+    1 and 2 above on a medium customarily used for software interchange; or,\r
+\r
+    b) Accompany it with a written offer, valid for at least three\r
+    years, to give any third party, for a charge no more than your\r
+    cost of physically performing source distribution, a complete\r
+    machine-readable copy of the corresponding source code, to be\r
+    distributed under the terms of Sections 1 and 2 above on a medium\r
+    customarily used for software interchange; or,\r
+\r
+    c) Accompany it with the information you received as to the offer\r
+    to distribute corresponding source code.  (This alternative is\r
+    allowed only for noncommercial distribution and only if you\r
+    received the program in object code or executable form with such\r
+    an offer, in accord with Subsection b above.)\r
+\r
+ The source code for a work means the preferred form of the work for\r
+ making modifications to it.  For an executable work, complete source\r
+ code means all the source code for all modules it contains, plus any\r
+ associated interface definition files, plus the scripts used to\r
+ control compilation and installation of the executable.  However, as a\r
+ special exception, the source code distributed need not include\r
+ anything that is normally distributed (in either source or binary\r
+ form) with the major components (compiler, kernel, and so on) of the\r
+ operating system on which the executable runs, unless that component\r
+ itself accompanies the executable.\r
+\r
+ If distribution of executable or object code is made by offering\r
+ access to copy from a designated place, then offering equivalent\r
+ access to copy the source code from the same place counts as\r
+ distribution of the source code, even though third parties are not\r
+ compelled to copy the source along with the object code.\r
+\r
+  4. You may not copy, modify, sublicense, or distribute the Program\r
+ except as expressly provided under this License.  Any attempt\r
+ otherwise to copy, modify, sublicense or distribute the Program is\r
+ void, and will automatically terminate your rights under this License.\r
+ However, parties who have received copies, or rights, from you under\r
+ this License will not have their licenses terminated so long as such\r
+ parties remain in full compliance.\r
+\r
+  5. You are not required to accept this License, since you have not\r
+ signed it.  However, nothing else grants you permission to modify or\r
+ distribute the Program or its derivative works.  These actions are\r
+ prohibited by law if you do not accept this License.  Therefore, by\r
+ modifying or distributing the Program (or any work based on the\r
+ Program), you indicate your acceptance of this License to do so, and\r
+ all its terms and conditions for copying, distributing or modifying\r
+ the Program or works based on it.\r
+\r
+  6. Each time you redistribute the Program (or any work based on the\r
+ Program), the recipient automatically receives a license from the\r
+ original licensor to copy, distribute or modify the Program subject to\r
+ these terms and conditions.  You may not impose any further\r
+ restrictions on the recipients' exercise of the rights granted herein.\r
+ You are not responsible for enforcing compliance by third parties to\r
+ this License.\r
+\r
+  7. If, as a consequence of a court judgment or allegation of patent\r
+ infringement or for any other reason (not limited to patent issues),\r
+ conditions are imposed on you (whether by court order, agreement or\r
+ otherwise) that contradict the conditions of this License, they do not\r
+ excuse you from the conditions of this License.  If you cannot\r
+ distribute so as to satisfy simultaneously your obligations under this\r
+ License and any other pertinent obligations, then as a consequence you\r
+ may not distribute the Program at all.  For example, if a patent\r
+ license would not permit royalty-free redistribution of the Program by\r
+ all those who receive copies directly or indirectly through you, then\r
+ the only way you could satisfy both it and this License would be to\r
+ refrain entirely from distribution of the Program.\r
+\r
+ If any portion of this section is held invalid or unenforceable under\r
+ any particular circumstance, the balance of the section is intended to\r
+ apply and the section as a whole is intended to apply in other\r
+ circumstances.\r
+\r
+ It is not the purpose of this section to induce you to infringe any\r
+ patents or other property right claims or to contest validity of any\r
+ such claims; this section has the sole purpose of protecting the\r
+ integrity of the free software distribution system, which is\r
+ implemented by public license practices.  Many people have made\r
+ generous contributions to the wide range of software distributed\r
+ through that system in reliance on consistent application of that\r
+ system; it is up to the author/donor to decide if he or she is willing\r
+ to distribute software through any other system and a licensee cannot\r
+ impose that choice.\r
+\r
+ This section is intended to make thoroughly clear what is believed to\r
+ be a consequence of the rest of this License.\r
+\r
+  8. If the distribution and/or use of the Program is restricted in\r
+ certain countries either by patents or by copyrighted interfaces, the\r
+ original copyright holder who places the Program under this License\r
+ may add an explicit geographical distribution limitation excluding\r
+ those countries, so that distribution is permitted only in or among\r
+ countries not thus excluded.  In such case, this License incorporates\r
+ the limitation as if written in the body of this License.\r
+\r
+  9. The Free Software Foundation may publish revised and/or new versions\r
+ of the General Public License from time to time.  Such new versions will\r
+ be similar in spirit to the present version, but may differ in detail to\r
+ address new problems or concerns.\r
+\r
+ Each version is given a distinguishing version number.  If the Program\r
+ specifies a version number of this License which applies to it and "any\r
+ later version", you have the option of following the terms and conditions\r
+ either of that version or of any later version published by the Free\r
+ Software Foundation.  If the Program does not specify a version number of\r
+ this License, you may choose any version ever published by the Free Software\r
+ Foundation.\r
+\r
+  10. If you wish to incorporate parts of the Program into other free\r
+ programs whose distribution conditions are different, write to the author\r
+ to ask for permission.  For software which is copyrighted by the Free\r
+ Software Foundation, write to the Free Software Foundation; we sometimes\r
+ make exceptions for this.  Our decision will be guided by the two goals\r
+ of preserving the free status of all derivatives of our free software and\r
+ of promoting the sharing and reuse of software generally.\r
+\r
+                           NO WARRANTY\r
+\r
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\r
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\r
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\r
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
+ REPAIR OR CORRECTION.\r
+\r
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
+ POSSIBILITY OF SUCH DAMAGES.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/INSTALL b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/INSTALL
new file mode 100644 (file)
index 0000000..68ed5c9
--- /dev/null
@@ -0,0 +1,13 @@
+If you have obtained an EventMachine source-tarball (.tar.gz):\r
+unzip and untar the tarball, and enter the directory that is\r
+created. In that directory, say:\r
+ruby setup.rb\r
+(You may need to be root to execute this command.)\r
+\r
+To create documentation for EventMachine, simply type:\r
+rake rdoc\r
+in the distro directory. Rdocs will be created in subdirectory rdoc.\r
+\r
+If you have obtained a gem version of EventMachine, install it in the\r
+usual way (gem install eventmachine). You may need superuser privileges\r
+to execute this command.\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/KEYBOARD b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/KEYBOARD
new file mode 100644 (file)
index 0000000..f1e10e5
--- /dev/null
@@ -0,0 +1,38 @@
+EventMachine (EM) can respond to keyboard events. This gives your event-driven programs the ability to respond to input from local users.\r
+\r
+Programming EM to handle keyboard input in Ruby is simplicity itself. Just use EventMachine#open_keyboard, and supply the name of a Ruby module or class that will receive the input:\r
+\r
+ require 'rubygems'\r
+ require 'eventmachine'\r
\r
+ module MyKeyboardHandler\r
+       def receive_data keystrokes\r
+               puts "I received the following data from the keyboard: #{keystrokes}"\r
+       end\r
+ end\r
\r
+ EM.run {\r
+       EM.open_keyboard(MyKeyboardHandler)\r
+ }\r
+\r
+\r
+If you want EM to send line-buffered keyboard input to your program, just include the LineText2 protocol module in your handler class or module:\r
+\r
+\r
+\r
+ require 'rubygems'\r
+ require 'eventmachine'\r
\r
+ module MyKeyboardHandler\r
+       include EM::Protocols::LineText2\r
+       def receive_line data\r
+               puts "I received the following line from the keyboard: #{data}"\r
+       end\r
+ end\r
\r
+ EM.run {\r
+       EM.open_keyboard(MyKeyboardHandler)\r
+ }\r
+\r
+As we said, simplicity itself. You can call EventMachine#open_keyboard at any time while the EM reactor loop is running. In other words, the method invocation may appear anywhere in an EventMachine#run block, or in any code invoked in the #run block.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LEGAL b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LEGAL
new file mode 100644 (file)
index 0000000..0e05196
--- /dev/null
@@ -0,0 +1,25 @@
+LEGAL NOTICE INFORMATION\r
+------------------------\r
+\r
+EventMachine is Copyright (C) 2006-07 by Francis Cianfrocca.\r
+\r
+EventMachine is copyrighted software owned by Francis Cianfrocca\r
+(blackhedd ... gmail.com). You may redistribute and/or modify this\r
+software as long as you comply with either the terms of the GPL\r
+(see the file GPL), or Ruby's license (see the file COPYING).\r
+\r
+Your use of all the files in this distribution is controlled by these\r
+license terms, except for those files specifically mentioned below:\r
+\r
+\r
+\r
+setup.rb\r
+       This file is Copyright (C) 2000-2005 by Minero Aoki\r
+       You can distribute/modify this file under the terms of\r
+       the GNU LGPL, Lesser General Public License version 2.1.\r
+\r
+\r
+lib/em/buftok.rb\r
+       This file is Copyright (C) 2007 by Tony Arcieri. This file is\r
+       covered by the terms of Ruby's License (see the file COPYING).\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LIGHTWEIGHT_CONCURRENCY b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/LIGHTWEIGHT_CONCURRENCY
new file mode 100644 (file)
index 0000000..d8e7db1
--- /dev/null
@@ -0,0 +1,70 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use them.\r
+\r
+\r
+=== What is Lightweight Concurrency?\r
+\r
+We use the term "Lightweight Concurrency" (LC) to refer to concurrency mechanisms that are lighter than Ruby threads. By "lighter," we mean: less resource-intensive in one or more dimensions, usually including memory and CPU usage. In general, you turn to LC in the hope of improving the performance and scalability of your programs.\r
+\r
+In addition to the two EventMachine mechanisms we will discuss here, Ruby has at least one other LC construct: Fibers, which are currently under development in Ruby 1.9.\r
+\r
+The technical feature that makes all of these LC mechanisms different from standard Ruby threads is that they are not scheduled automatically.\r
+\r
+When you create and run Ruby threads, you can assume (within certain constraints) that your threads will all be scheduled fairly by Ruby's runtime. Ruby itself is responsible for giving each of your threads its own share of the total runtime.\r
+\r
+But with LC, your program is responsible for causing different execution paths to run. In effect, your program has to act as a "thread scheduler." Scheduled entities in LC run to completion and are never preempted. The runtime system has far less work to do since it has no need to interrupt threads or to schedule them fairly. This is what makes LC lighter and faster.\r
+\r
+You'll learn exactly how LC scheduling works in practice as we work through specific examples.\r
+\r
+\r
+=== EventMachine Lightweight Concurrency\r
+\r
+Recall that EM provides a reactor loop that must be running in order for your programs to perform event-driven logic. An EM program typically has a structure like this:\r
+\r
+ require 'eventmachine'\r
+\r
+ # your initializations\r
+\r
+ EM.run {\r
+     # perform event-driven I/O here, including network clients,\r
+     # servers, timers, and thread-pool operations.\r
+ }\r
+\r
+ # your cleanup\r
+ # end of the program\r
+\r
+\r
+EventMachine#run executes the reactor loop, which causes your code to be called as events of interest to your program occur. The block you pass to EventMachine#run is executed right after the reactor loop starts, and is the right place to start socket acceptors, etc.\r
+\r
+Because the reactor loop runs constantly in an EM program (until it is stopped by a call to EventMachine#stop), it has the ability to schedule blocks of code for asynchronous execution. Unlike a pre-emptive thread scheduler, it's NOT able to interrupt code blocks while they execute. But the scheduling capability it does have is enough to enable lightweight concurrency.\r
+\r
+\r
+For information on Spawned Processes, see the separate document SPAWNED_PROCESSES.\r
+\r
+For information on Deferrables, see the separate document DEFERRABLES.\r
+\r
+\r
+=== [SIDEBAR]: I Heard That EventMachine Doesn't Work With Ruby Threads.\r
+\r
+This is incorrect. EM is fully interoperable with all versions of Ruby threads, and has been since its earliest releases.\r
+\r
+It's very true that EM encourages an "evented" (non-threaded) programming style. The specific benefits of event-driven programming are far better performance and scalabiity for well-written programs, and far easier debugging.\r
+\r
+The benefit of using threads for similar applications is a possibly more intuitive programming model, as well as the fact that threads are already familiar to most programmers. Also, bugs in threaded programs often fail to show up until programs go into production. These factors create the illusion that threaded programs are easier to write.\r
+\r
+However, some operations that occur frequently in professional-caliber applications simply can't be done without threads. (The classic example is making calls to database client-libraries that block on network I/O until they complete.)\r
+\r
+EventMachine not only allows the use of Ruby threads in these cases, but it even provides a built-in thread-pool object to make them easier to work with.\r
+\r
+You may have heard a persistent criticism that evented I/O is fundamentally incompatible with Ruby threads. It is true that some well-publicized attempts to incorporate event-handling libraries into Ruby were not successful. But EventMachine was designed from the ground up with Ruby compatibility in mind, so EM never suffered from the problems that defeated the earlier attempts.\r
+\r
+\r
+=== [SIDEBAR]: I Heard That EventMachine Doesn't Work Very Well On Windows.\r
+\r
+This too is incorrect. EventMachine is an extension written in C++ and Java, and therefore it requires compilation. Many Windows computers (and some Unix computers, especially in production environments) don't have a build stack. Attempting to install EventMachine on a machine without a compiler usually produces a confusing error.\r
+\r
+In addition, Ruby has a much-debated issue with Windows compiler versions. Ruby on Windows works best with Visual Studio 6, a compiler version that is long out-of-print, no longer supported by Microsoft, and difficult to obtain. (This problem is not specific to EventMachine.)\r
+\r
+Shortly after EventMachine was first released, the compiler issues led to criticism that EM was incompatible with Windows. Since that time, every EventMachine release has been supplied in a precompiled binary form for Windows users, that does not require you to compile the code yourself. EM binary Gems for Windows are compiled using Visual Studio 6.\r
+\r
+EventMachine does supply some advanced features (such as Linux EPOLL support, reduced-privilege operation, UNIX-domain sockets, etc.) that have no meaningful implementation on Windows. Apart from these special cases, all EM functionality (including lightweight concurrency) works perfectly well on Windows.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/PURE_RUBY b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/PURE_RUBY
new file mode 100644 (file)
index 0000000..49db808
--- /dev/null
@@ -0,0 +1,75 @@
+EventMachine is supplied in three alternative versions.\r
+\r
+1) A version that includes a Ruby extension written in C++. This version requires compilation;\r
+2) A version for JRuby that contains a precompiled JAR file written in Java;\r
+3) A pure Ruby version that has no external dependencies and can run in any Ruby environment.\r
+\r
+The Java version of EventMachine is packaged in a distinct manner and must be installed using a\r
+special procedure. This version is described fully in a different document, and not considered\r
+further here.\r
+\r
+The C++ and pure-Ruby versions, however, are shipped in the same distribution. You use the same\r
+files (either tarball or Ruby gem) to install both of these versions.\r
+\r
+If you intend to use the C++ version, you must successfully compile EventMachine after you install it.\r
+(The gem installation attempts to perform this step automatically.)\r
+\r
+If you choose not to compile the EventMachine C++ extension, or if your compilation fails for any\r
+reason, you still have a fully-functional installation of the pure-Ruby version of EM.\r
+\r
+However, for technical reasons, a default EM installation (whether or not the compilation succeeds)\r
+will always assume that the compiled ("extension") implementation should be used.\r
+\r
+If you want your EM program to use the pure Ruby version, you must specifically request it. There\r
+are two ways to do this: by setting either a Ruby global variable, or an environment string.\r
+\r
+The following code will invoke the pure-Ruby implementation of EM:\r
+\r
+ $eventmachine_library = :pure_ruby\r
+ require 'eventmachine'\r
+\r
+ EM.library_type #=> "pure_ruby"\r
+\r
+Notice that this requires a code change and is not the preferred way to select pure Ruby, unless\r
+for some reason you are absolutely sure you will never want the compiled implementation.\r
+\r
+Setting the following environment string has the same effect:\r
+\r
+ export EVENTMACHINE_LIBRARY="pure_ruby"\r
+\r
+This technique gives you the flexibility to select either version at runtime with no code changes.\r
+\r
+Support\r
+\r
+The EventMachine development team has committed to support precisely the same APIs for all the\r
+various implementations of EM.\r
+\r
+This means that you can expect any EM program to behave identically, whether you use pure Ruby,\r
+the compiled C++ extension, or JRuby. Deviations from this behavior are to be considered bugs\r
+and should be reported as such.\r
+\r
+There is a small number of exceptions to this rule, which arise from underlying platform\r
+distinctions. Notably, EM#epoll is a silent no-op in the pure Ruby implementation.\r
+\r
+\r
+When Should You Use the Pure-Ruby Implementation of EM?\r
+\r
+\r
+Use the pure Ruby implementation of EM when you must support a platform for which no C++ compiler\r
+is available, or on which the standard EM C++ code can't be compiled.\r
+\r
+Keep in mind that you don't need a C++ compiler in order to deploy EM applications that rely on\r
+the compiled version, so long as appropriate C++ runtime libraries are available on the target platform.\r
+\r
+In extreme cases, you may find that you can develop software with the compiled EM version, but are\r
+not allowed to install required runtime libraries on the deployment system(s). This would be another\r
+case in which the pure Ruby implementation can be useful.\r
+\r
+In general you should avoid the pure Ruby version of EM when performance and scalability are important.\r
+EM in pure Ruby will necessarily run slower than the compiled version. Depending on your application\r
+this may or may not be a key issue.\r
+\r
+Also, since EPOLL is not supported in pure Ruby, your applications will be affected by Ruby's built-in\r
+limit of 1024 file and socket descriptors that may be open in a single process. For maximum scalability\r
+and performance, always use EPOLL if possible.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/RELEASE_NOTES b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/RELEASE_NOTES
new file mode 100644 (file)
index 0000000..ae551e1
--- /dev/null
@@ -0,0 +1,94 @@
+RUBY/EventMachine RELEASE NOTES\r
+\r
+--------------------------------------------------\r
+Version: 0.9.0, released xxXXX07\r
+Added Erlang-like distributed-computing features\r
+\r
+--------------------------------------------------\r
+Version: 0.8.0, released 23Jun07\r
+Added an epoll implementation for Linux 2.6 kernels.\r
+Added evented #popen.\r
+\r
+--------------------------------------------------\r
+Version: 0.7.3, released 22May07\r
+Added a large variety of small features. See the ChangeLog.\r
+\r
+--------------------------------------------------\r
+Version: 0.7.1, released xxNov06\r
+Added protocol handlers for line-oriented protocols.\r
+Various bug fixes.\r
+\r
+--------------------------------------------------\r
+Version: 0.7.0, released 20Nov06\r
+Added a fix in em.cpp/ConnectToServer to fix a fatal exception that\r
+occurred in FreeBSD when connecting successfully to a remote server.\r
+\r
+--------------------------------------------------\r
+Version: 0.6.0, released xxJul06\r
+Added deferred operations, suggested by Don Stocks, amillionhitpoints@yahoo.com.\r
+\r
+--------------------------------------------------\r
+Version: 0.5.4, released xxJun06\r
+Added get_peername support for streams and datagrams.\r
+\r
+--------------------------------------------------\r
+Version: 0.5.3, released 17May06\r
+Fixed bugs in extconf.rb, thanks to Daniel Harple, dharple@generalconsumption.org.\r
+Added proper setup.rb and rake tasks, thanks to Austin Ziegler.\r
+Fixed a handful of reported problems with builds on various platforms.\r
+\r
+--------------------------------------------------\r
+Version: 0.5.2, released 05May06\r
+Made several nonvisible improvements to the Windows\r
+implementation.\r
+Added an exception-handling patch contributed by Jeff Rose, jeff@rosejn.net.\r
+Added a dir-config patch contributed anonymously.\r
+Supported builds on Solaris.\r
+\r
+--------------------------------------------------\r
+Version: 0.5.1, released 05May06\r
+Made it possible to pass a Class rather than a Module\r
+to a protocol handler.\r
+Added Windows port.\r
+\r
+--------------------------------------------------\r
+Version: 0.5.0, released 30Apr06\r
+Added a preliminary SSL/TLS extension. This will probably\r
+change over the next few releases.\r
+\r
+--------------------------------------------------\r
+Version: 0.4.5, released 29Apr06\r
+Changed ext files so the ruby.h is installed after unistd.h\r
+otherwise it doesn't compile on gcc 4.1\r
+\r
+--------------------------------------------------\r
+Version: 0.4.2, released 19Apr06\r
+Changed the Ruby-glue so the extension will play nicer\r
+in the sandbox with Ruby threads.\r
+Added an EventMachine::run_without_threads API to\r
+switch off the thread-awareness for better performance\r
+in programs that do not spin any Ruby threads.\r
+\r
+--------------------------------------------------\r
+Version: 0.4.1, released 15Apr06\r
+Reworked the shared-object interface to make it easier to\r
+use EventMachine from languages other than Ruby.\r
+\r
+--------------------------------------------------\r
+Version: 0.3.2, released 12Apr06\r
+Added support for a user-supplied block in EventMachine#connect.\r
+\r
+--------------------------------------------------\r
+Version: 0.3.1, released 11Apr06\r
+Fixed bug that prevented EventMachine from being run multiple\r
+times in a single process.\r
+\r
+--------------------------------------------------\r
+Version: 0.3.0, released 10Apr06\r
+Added method EventHandler::Connection::post_init\r
+\r
+--------------------------------------------------\r
+Version: 0.2.0, released 10Apr06\r
+Added method EventHandler::stop\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SMTP b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SMTP
new file mode 100644 (file)
index 0000000..a2c1474
--- /dev/null
@@ -0,0 +1,2 @@
+This note details the usage of EventMachine's built-in support for SMTP. EM supports both client and server connections, which will be described in separate sections.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SPAWNED_PROCESSES b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/SPAWNED_PROCESSES
new file mode 100644 (file)
index 0000000..b8fd355
--- /dev/null
@@ -0,0 +1,89 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use spawned processes. For more information, see the separate document LIGHTWEIGHT_CONCURRENCY.\r
+\r
+\r
+=== What are Spawned Processes?\r
+\r
+Spawned Processes in EventMachine are inspired directly by the "processes" found in the Erlang programming language. EM deliberately borrows much (but not all) of Erlang's terminology. However, EM's spawned processes differ from Erlang's in ways that reflect not only Ruby style, but also the fact that Ruby is not a functional language like Erlang.\r
+\r
+Let's proceed with a complete, working code sample that we will analyze line by line. Here's an EM implementation of the "ping-pong" program that also appears in the Erlang tutorial:\r
+\r
+\r
+ require 'eventmachine'\r
\r
+ EM.run {\r
+   pong = EM.spawn {|x, ping|\r
+     puts "Pong received #{x}"\r
+     ping.notify( x-1 )\r
+   }\r
\r
+   ping = EM.spawn {|x|\r
+     if x > 0\r
+       puts "Pinging #{x}"\r
+       pong.notify x, self\r
+     else\r
+       EM.stop\r
+     end\r
+   }\r
\r
+   ping.notify 3\r
+ }\r
+\r
+If you run this program, you'll see the following output:\r
+\r
+ Pinging 3\r
+ Pong received 3\r
+ Pinging 2\r
+ Pong received 2\r
+ Pinging 1\r
+ Pong received 1\r
+\r
+Let's take it step by step.\r
+\r
+EventMachine#spawn works very much like the built-in function spawn in Erlang. It returns a reference to a Ruby object of class EventMachine::SpawnedProcess, which is actually a schedulable entity. In Erlang, the value returned from spawn is called a "process identifier" or "pid." But we'll refer to the Ruby object returned from EM#spawn simply as a "spawned process."\r
+\r
+You pass a Ruby block with zero or more parameters to EventMachine#spawn. Like all Ruby blocks, this one is a closure, so it can refer to variables defined in the local context when you call EM#spawn.\r
+\r
+However, the code block passed to EM#spawn does NOT execute immediately by default. Rather, it will execute only when the Spawned Object is "notified." In Erlang, this process is called "message passing," and is done with the operator !, but in Ruby it's done simply by calling the #notify method of a spawned-process object. The parameters you pass to #notify must match those defined in the block that was originally passed to EM#spawn.\r
+\r
+When you call the #notify method of a spawned-process object, EM's reactor core will execute the code block originally passed to EM#spawn, at some point in the future. (#notify itself merely adds a notification to the object's message queue and ALWAYS returns immediately.)\r
+\r
+When a SpawnedProcess object executes a notification, it does so in the context of the SpawnedProcess object itself. The notified code block can see local context from the point at which EM#spawn was called. However, the value of "self" inside the notified code block is a reference to the SpawnedProcesss object itself.\r
+\r
+An EM spawned process is nothing more than a Ruby object with a message queue attached to it. You can have any number of spawned processes in your program without compromising scalability. You can notify a spawned process any number of times, and each notification will cause a "message" to be placed in the queue of the spawned process. Spawned processes with non-empty message queues are scheduled for execution automatically by the EM reactor. Spawned processes with no visible references are garbage-collected like any other Ruby object.\r
+\r
+Back to our code sample:\r
+\r
+   pong = EM.spawn {|x, ping|\r
+     puts "Pong received #{x}"\r
+     ping.notify( x-1 )\r
+   }\r
+\r
+This simply creates a spawned process and assigns it to the local variable pong. You can see that the spawned code block takes a numeric parameter and a reference to another spawned process. When pong is notified, it expects to receive arguments corresponding to these two parameters. It simply prints out the number it receives as the first argument. Then it notifies the spawned process referenced by the second argument, passing it the first argument minus 1.\r
+\r
+And then the block ends, which is crucial because otherwise nothing else can run. (Remember that in LC, scheduled entities run to completion and are never preempted.)\r
+\r
+On to the next bit of the code sample:\r
+\r
+   ping = EM.spawn {|x|\r
+     if x > 0\r
+       puts "Pinging #{x}"\r
+       pong.notify x, self\r
+     else\r
+       EM.stop\r
+     end\r
+   }\r
+\r
+Here, we're spawning a process that takes a single (numeric) parameter. If the parameter is greater than zero, the block writes it to the console. It then notifies the spawned process referenced by the pong local variable, passing as arguments its number argument, and a reference to itself. The latter reference, as you saw above, is used by pong to send a return notification.\r
+\r
+If the ping process receives a zero value, it will stop the reactor loop and end the program.\r
+\r
+Now we've created a pair of spawned processes, but nothing else has happened. If we stop now, the program will spin in the EM reactor loop, doing nothing at all. Our spawned processes will never be scheduled for execution.\r
+\r
+But look at the next line in the code sample:\r
+\r
+   ping.notify 3\r
+\r
+This line gets the ping-pong ball rolling. We call ping's #notify method, passing the argument 3. This causes a message to be sent to the ping spawned process. The message contains the single argument, and it causes the EM reactor to schedule the ping process. And this in turn results in the execution of the Ruby code block passed to EM#spawn when ping was created. Everything else proceeds as a result of the messages that are subsequently passed to each other by the spawned processes.\r
+\r
+[TODO, present the outbound network i/o use case, and clarify that spawned processes are interleaved with normal i/o operations and don't interfere with them at all. Also, blame Erlang for the confusing term "process"]\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/TODO b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/docs/TODO
new file mode 100644 (file)
index 0000000..73819fb
--- /dev/null
@@ -0,0 +1,8 @@
+TODO List:\r
+\r
+12Aug06: Noticed by Don Stocks. A TCP connect-request that results\r
+in a failed DNS resolution fires a fatal error back to user code.\r
+Uuuuuugly. We should probably cause an unbind event to get fired\r
+instead, and add some parameterization so the caller can detect\r
+the nature of the failure.\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/eventmachine.gemspec b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/eventmachine.gemspec
new file mode 100644 (file)
index 0000000..647174f
--- /dev/null
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{eventmachine}
+  s.version = "0.12.10"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["Francis Cianfrocca"]
+  s.date = %q{2009-10-24}
+  s.description = %q{EventMachine implements a fast, single-threaded engine for arbitrary network
+communications. It's extremely easy to use in Ruby. EventMachine wraps all
+interactions with IP sockets, allowing programs to concentrate on the
+implementation of network protocols. It can be used to create both network
+servers and clients. To create a server or client, a Ruby program only needs
+to specify the IP address and port, and provide a Module that implements the
+communications protocol. Implementations of several standard network protocols
+are provided with the package, primarily to serve as examples. The real goal
+of EventMachine is to enable programs to easily interface with other programs
+using TCP/IP, especially if custom protocols are required.
+}
+  s.email = %q{garbagecat10@gmail.com}
+  s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"]
+  s.files = [".gitignore", "README", "Rakefile", "docs/COPYING", "docs/ChangeLog", "docs/DEFERRABLES", "docs/EPOLL", "docs/GNU", "docs/INSTALL", "docs/KEYBOARD", "docs/LEGAL", "docs/LIGHTWEIGHT_CONCURRENCY", "docs/PURE_RUBY", "docs/RELEASE_NOTES", "docs/SMTP", "docs/SPAWNED_PROCESSES", "docs/TODO", "eventmachine.gemspec", "examples/ex_channel.rb", "examples/ex_queue.rb", "examples/helper.rb", "ext/binder.cpp", "ext/binder.h", "ext/cmain.cpp", "ext/cplusplus.cpp", "ext/ed.cpp", "ext/ed.h", "ext/em.cpp", "ext/em.h", "ext/emwin.cpp", "ext/emwin.h", "ext/epoll.cpp", "ext/epoll.h", "ext/eventmachine.h", "ext/eventmachine_cpp.h", "ext/extconf.rb", "ext/fastfilereader/extconf.rb", "ext/fastfilereader/mapper.cpp", "ext/fastfilereader/mapper.h", "ext/fastfilereader/rubymain.cpp", "ext/files.cpp", "ext/files.h", "ext/kb.cpp", "ext/page.cpp", "ext/page.h", "ext/pipe.cpp", "ext/project.h", "ext/rubymain.cpp", "ext/sigs.cpp", "ext/sigs.h", "ext/ssl.cpp", "ext/ssl.h", "java/.classpath", "java/.project", "java/src/com/rubyeventmachine/EmReactor.java", "java/src/com/rubyeventmachine/EmReactorException.java", "java/src/com/rubyeventmachine/EventableChannel.java", "java/src/com/rubyeventmachine/EventableDatagramChannel.java", "java/src/com/rubyeventmachine/EventableSocketChannel.java", "java/src/com/rubyeventmachine/application/Application.java", "java/src/com/rubyeventmachine/application/Connection.java", "java/src/com/rubyeventmachine/application/ConnectionFactory.java", "java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java", "java/src/com/rubyeventmachine/application/PeriodicTimer.java", "java/src/com/rubyeventmachine/application/Timer.java", "java/src/com/rubyeventmachine/tests/ApplicationTest.java", "java/src/com/rubyeventmachine/tests/ConnectTest.java", "java/src/com/rubyeventmachine/tests/EMTest.java", "java/src/com/rubyeventmachine/tests/TestDatagrams.java", "java/src/com/rubyeventmachine/tests/TestServers.java", "java/src/com/rubyeventmachine/tests/TestTimers.java", "lib/em/buftok.rb", "lib/em/callback.rb", "lib/em/channel.rb", "lib/em/connection.rb", "lib/em/deferrable.rb", "lib/em/file_watch.rb", "lib/em/future.rb", "lib/em/messages.rb", "lib/em/process_watch.rb", "lib/em/processes.rb", "lib/em/protocols.rb", "lib/em/protocols/header_and_content.rb", "lib/em/protocols/httpclient.rb", "lib/em/protocols/httpclient2.rb", "lib/em/protocols/line_and_text.rb", "lib/em/protocols/linetext2.rb", "lib/em/protocols/memcache.rb", "lib/em/protocols/object_protocol.rb", "lib/em/protocols/postgres3.rb", "lib/em/protocols/saslauth.rb", "lib/em/protocols/smtpclient.rb", "lib/em/protocols/smtpserver.rb", "lib/em/protocols/socks4.rb", "lib/em/protocols/stomp.rb", "lib/em/protocols/tcptest.rb", "lib/em/queue.rb", "lib/em/spawnable.rb", "lib/em/streamer.rb", "lib/em/timers.rb", "lib/em/version.rb", "lib/eventmachine.rb", "lib/evma.rb", "lib/evma/callback.rb", "lib/evma/container.rb", "lib/evma/factory.rb", "lib/evma/protocol.rb", "lib/evma/reactor.rb", "lib/jeventmachine.rb", "lib/pr_eventmachine.rb", "setup.rb", "tasks/cpp.rake_example", "tests/client.crt", "tests/client.key", "tests/test_attach.rb", "tests/test_basic.rb", "tests/test_channel.rb", "tests/test_connection_count.rb", "tests/test_defer.rb", "tests/test_epoll.rb", "tests/test_error_handler.rb", "tests/test_errors.rb", "tests/test_exc.rb", "tests/test_file_watch.rb", "tests/test_futures.rb", "tests/test_get_sock_opt.rb", "tests/test_handler_check.rb", "tests/test_hc.rb", "tests/test_httpclient.rb", "tests/test_httpclient2.rb", "tests/test_inactivity_timeout.rb", "tests/test_kb.rb", "tests/test_ltp.rb", "tests/test_ltp2.rb", "tests/test_next_tick.rb", "tests/test_object_protocol.rb", "tests/test_pause.rb", "tests/test_pending_connect_timeout.rb", "tests/test_process_watch.rb", "tests/test_processes.rb", "tests/test_proxy_connection.rb", "tests/test_pure.rb", "tests/test_queue.rb", "tests/test_running.rb", "tests/test_sasl.rb", "tests/test_send_file.rb", "tests/test_servers.rb", "tests/test_smtpclient.rb", "tests/test_smtpserver.rb", "tests/test_spawn.rb", "tests/test_ssl_args.rb", "tests/test_ssl_methods.rb", "tests/test_ssl_verify.rb", "tests/test_timers.rb", "tests/test_ud.rb", "tests/testem.rb", "web/whatis"]
+  s.homepage = %q{http://rubyeventmachine.com}
+  s.rdoc_options = ["--title", "EventMachine", "--main", "README", "--line-numbers", "-x", "lib/em/version", "-x", "lib/emva", "-x", "lib/evma/", "-x", "lib/pr_eventmachine", "-x", "lib/jeventmachine"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{eventmachine}
+  s.rubygems_version = %q{1.3.5}
+  s.summary = %q{Ruby/EventMachine library}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 3
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_channel.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_channel.rb
new file mode 100644 (file)
index 0000000..ddeee29
--- /dev/null
@@ -0,0 +1,43 @@
+require File.dirname(__FILE__) + '/helper'\r
+\r
+EM.run do\r
+\r
+  # Create a channel to push data to, this could be stocks...\r
+  RandChannel = EM::Channel.new\r
+\r
+  # The server simply subscribes client connections to the channel on connect,\r
+  # and unsubscribes them on disconnect.\r
+  class Server < EM::Connection\r
+    def self.start(host = '127.0.0.1', port = 8000)\r
+      EM.start_server(host, port, self)\r
+    end\r
+\r
+    def post_init\r
+      @sid = RandChannel.subscribe { |m| send_data "#{m.inspect}\n" }\r
+    end\r
+\r
+    def unbind\r
+      RandChannel.unsubscribe @sid\r
+    end\r
+  end\r
+  Server.start\r
+\r
+  # Two client connections, that just print what they receive.\r
+  2.times do\r
+    EM.connect('127.0.0.1', 8000) do |c|\r
+      c.extend EM::P::LineText2\r
+      def c.receive_line(line)\r
+        puts "Subscriber: #{signature} got #{line}"\r
+      end\r
+      EM.add_timer(2) { c.close_connection }\r
+    end\r
+  end\r
+\r
+  # This part of the example is more fake, but imagine sleep was in fact a \r
+  # long running calculation to achieve the value.\r
+  40.times do\r
+    EM.defer lambda { v = sleep(rand * 2); RandChannel << [Time.now, v] }\r
+  end\r
+  \r
+  EM.add_timer(5) { EM.stop }\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_queue.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/ex_queue.rb
new file mode 100644 (file)
index 0000000..f7393db
--- /dev/null
@@ -0,0 +1,2 @@
+require File.dirname(__FILE__) + '/helper'\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/helper.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/examples/helper.rb
new file mode 100644 (file)
index 0000000..2ed05e9
--- /dev/null
@@ -0,0 +1,2 @@
+$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')\r
+require 'eventmachine'
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.cpp
new file mode 100644 (file)
index 0000000..0b12762
--- /dev/null
@@ -0,0 +1,125 @@
+/*****************************************************************************
+
+$Id$
+
+File:     binder.cpp
+Date:     07Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+
+#define DEV_URANDOM "/dev/urandom"
+
+
+map<unsigned long, Bindable_t*> Bindable_t::BindingBag;
+
+
+/********************************
+STATIC Bindable_t::CreateBinding
+********************************/
+
+unsigned long Bindable_t::CreateBinding()
+{
+       // XXX use atomic_t to prevent thread-safety issues
+       static unsigned long num = 0;
+       while(BindingBag[++num]);
+       return num;
+}
+
+#if 0
+string Bindable_t::CreateBinding()
+{
+       static int index = 0;
+       static string seed;
+
+       if ((index >= 1000000) || (seed.length() == 0)) {
+               #ifdef OS_UNIX
+               int fd = open (DEV_URANDOM, O_RDONLY);
+               if (fd < 0)
+                       throw std::runtime_error ("No entropy device");
+
+               unsigned char u[16];
+               size_t r = read (fd, u, sizeof(u));
+               if (r < sizeof(u))
+                       throw std::runtime_error ("Unable to read entropy device");
+
+               unsigned char *u1 = (unsigned char*)u;
+               char u2 [sizeof(u) * 2 + 1];
+
+               for (size_t i=0; i < sizeof(u); i++)
+                       sprintf (u2 + (i * 2), "%02x", u1[i]);
+
+               seed = string (u2);
+               #endif
+
+
+               #ifdef OS_WIN32
+               UUID uuid;
+               UuidCreate (&uuid);
+               unsigned char *uuidstring = NULL;
+               UuidToString (&uuid, &uuidstring);
+               if (!uuidstring)
+                       throw std::runtime_error ("Unable to read uuid");
+               seed = string ((const char*)uuidstring);
+
+               RpcStringFree (&uuidstring);
+               #endif
+
+               index = 0;
+
+
+       }
+
+       stringstream ss;
+       ss << seed << (++index);
+       return ss.str();
+}
+#endif
+
+/*****************************
+STATIC: Bindable_t::GetObject
+*****************************/
+
+Bindable_t *Bindable_t::GetObject (const unsigned long binding)
+{
+  map<unsigned long, Bindable_t*>::const_iterator i = BindingBag.find (binding);
+  if (i != BindingBag.end())
+    return i->second;
+  else
+    return NULL;
+}
+
+
+/**********************
+Bindable_t::Bindable_t
+**********************/
+
+Bindable_t::Bindable_t()
+{
+       Binding = Bindable_t::CreateBinding();
+       BindingBag [Binding] = this;
+}
+
+
+
+/***********************
+Bindable_t::~Bindable_t
+***********************/
+
+Bindable_t::~Bindable_t()
+{
+       BindingBag.erase (Binding);
+}
+
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/binder.h
new file mode 100644 (file)
index 0000000..20817b1
--- /dev/null
@@ -0,0 +1,46 @@
+/*****************************************************************************
+
+$Id$
+
+File:     binder.h
+Date:     07Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#ifndef __ObjectBindings__H_
+#define __ObjectBindings__H_
+
+
+class Bindable_t
+{
+       public:
+               static unsigned long CreateBinding();
+               static Bindable_t *GetObject (const unsigned long);
+               static map<unsigned long, Bindable_t*> BindingBag;
+
+       public:
+               Bindable_t();
+               virtual ~Bindable_t();
+
+               const unsigned long GetBinding() {return Binding;}
+
+       private:
+               unsigned long Binding;
+};
+
+
+
+
+
+#endif // __ObjectBindings__H_
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cmain.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cmain.cpp
new file mode 100644 (file)
index 0000000..d95b45d
--- /dev/null
@@ -0,0 +1,827 @@
+/*****************************************************************************
+
+$Id$
+
+File:                  cmain.cpp
+Date:                  06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+
+/* 21Sep09: ruby 1.9 defines macros for common i/o functions that point to rb_w32_* implementations.
+   We need to undef the stat to fix a build failure in evma_send_file_data_to_connection.
+   See http://groups.google.com/group/eventmachine/browse_thread/thread/fc60d9bb738ffc71
+*/
+#if defined(BUILD_FOR_RUBY) && defined(OS_WIN32)
+#undef stat
+#endif
+
+static EventMachine_t *EventMachine;
+static int bUseEpoll = 0;
+static int bUseKqueue = 0;
+
+extern "C" void ensure_eventmachine (const char *caller = "unknown caller")
+{
+       if (!EventMachine) {
+               const int err_size = 128;
+               char err_string[err_size];
+               snprintf (err_string, err_size, "eventmachine not initialized: %s", caller);
+               #ifdef BUILD_FOR_RUBY
+                       rb_raise(rb_eRuntimeError, "%s", err_string);
+               #else
+                       throw std::runtime_error (err_string);
+               #endif
+       }
+}
+
+/***********************
+evma_initialize_library
+***********************/
+
+extern "C" void evma_initialize_library (void(*cb)(const unsigned long, int, const char*, const unsigned long))
+{
+       // Probably a bad idea to mess with the signal mask of a process
+       // we're just being linked into.
+       //InstallSignalHandlers();
+       if (EventMachine)
+               #ifdef BUILD_FOR_RUBY
+                       rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_initialize_library");
+               #else
+                       throw std::runtime_error ("eventmachine already initialized: evma_initialize_library");
+               #endif
+       EventMachine = new EventMachine_t (cb);
+       if (bUseEpoll)
+               EventMachine->_UseEpoll();
+       if (bUseKqueue)
+               EventMachine->_UseKqueue();
+}
+
+
+/********************
+evma_release_library
+********************/
+
+extern "C" void evma_release_library()
+{
+       ensure_eventmachine("evma_release_library");
+       delete EventMachine;
+       EventMachine = NULL;
+}
+
+
+/****************
+evma_run_machine
+****************/
+
+extern "C" void evma_run_machine()
+{
+       ensure_eventmachine("evma_run_machine");
+       EventMachine->Run();
+}
+
+
+/**************************
+evma_install_oneshot_timer
+**************************/
+
+extern "C" const unsigned long evma_install_oneshot_timer (int seconds)
+{
+       ensure_eventmachine("evma_install_oneshot_timer");
+       return EventMachine->InstallOneshotTimer (seconds);
+}
+
+
+/**********************
+evma_connect_to_server
+**********************/
+
+extern "C" const unsigned long evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
+{
+       ensure_eventmachine("evma_connect_to_server");
+       return EventMachine->ConnectToServer (bind_addr, bind_port, server, port);
+}
+
+/***************************
+evma_connect_to_unix_server
+***************************/
+
+extern "C" const unsigned long evma_connect_to_unix_server (const char *server)
+{
+       ensure_eventmachine("evma_connect_to_unix_server");
+       return EventMachine->ConnectToUnixServer (server);
+}
+
+/**************
+evma_attach_fd
+**************/
+
+extern "C" const unsigned long evma_attach_fd (int file_descriptor, int watch_mode)
+{
+       ensure_eventmachine("evma_attach_fd");
+       return EventMachine->AttachFD (file_descriptor, watch_mode ? true : false);
+}
+
+/**************
+evma_detach_fd
+**************/
+
+extern "C" int evma_detach_fd (const unsigned long binding)
+{
+       ensure_eventmachine("evma_detach_fd");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               return EventMachine->DetachFD (ed);
+       else
+               #ifdef BUILD_FOR_RUBY
+                       rb_raise(rb_eRuntimeError, "invalid binding to detach");
+               #else
+                       throw std::runtime_error ("invalid binding to detach");
+               #endif
+}
+
+/************************
+evma_get_file_descriptor
+************************/
+
+extern "C" int evma_get_file_descriptor (const unsigned long binding)
+{
+       ensure_eventmachine("evma_get_file_descriptor");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               return ed->GetSocket();
+       else
+               #ifdef BUILD_FOR_RUBY
+                       rb_raise(rb_eRuntimeError, "invalid binding to get_fd");
+               #else
+                       throw std::runtime_error ("invalid binding to get_fd");
+               #endif
+}
+
+/***********************
+evma_is_notify_readable
+***********************/
+
+extern "C" int evma_is_notify_readable (const unsigned long binding)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->IsNotifyReadable() ? 1 : 0;
+       return -1;
+}
+
+/************************
+evma_set_notify_readable
+************************/
+
+extern "C" void evma_set_notify_readable (const unsigned long binding, int mode)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               cd->SetNotifyReadable (mode ? true : false);
+}
+
+/***********************
+evma_is_notify_writable
+***********************/
+
+extern "C" int evma_is_notify_writable (const unsigned long binding)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->IsNotifyWritable() ? 1 : 0;
+       return -1;
+}
+
+/************************
+evma_set_notify_writable
+************************/
+
+extern "C" void evma_set_notify_writable (const unsigned long binding, int mode)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               cd->SetNotifyWritable (mode ? true : false);
+}
+
+/**********
+evma_pause
+**********/
+
+extern "C" int evma_pause (const unsigned long binding)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->Pause() ? 1 : 0;
+
+       return 0;
+}
+
+/***********
+evma_resume
+***********/
+
+extern "C" int evma_resume (const unsigned long binding)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->Resume() ? 1 : 0;
+
+       return 0;
+}
+
+/**************
+evma_is_paused
+**************/
+
+extern "C" int evma_is_paused (const unsigned long binding)
+{
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->IsPaused() ? 1 : 0;
+
+       return 0;
+}
+
+/**********************
+evma_create_tcp_server
+**********************/
+
+extern "C" const unsigned long evma_create_tcp_server (const char *address, int port)
+{
+       ensure_eventmachine("evma_create_tcp_server");
+       return EventMachine->CreateTcpServer (address, port);
+}
+
+/******************************
+evma_create_unix_domain_server
+******************************/
+
+extern "C" const unsigned long evma_create_unix_domain_server (const char *filename)
+{
+       ensure_eventmachine("evma_create_unix_domain_server");
+       return EventMachine->CreateUnixDomainServer (filename);
+}
+
+/*************************
+evma_open_datagram_socket
+*************************/
+
+extern "C" const unsigned long evma_open_datagram_socket (const char *address, int port)
+{
+       ensure_eventmachine("evma_open_datagram_socket");
+       return EventMachine->OpenDatagramSocket (address, port);
+}
+
+/******************
+evma_open_keyboard
+******************/
+
+extern "C" const unsigned long evma_open_keyboard()
+{
+       ensure_eventmachine("evma_open_keyboard");
+       return EventMachine->OpenKeyboard();
+}
+
+/*******************
+evma_watch_filename
+*******************/
+
+extern "C" const unsigned long evma_watch_filename (const char *fname)
+{
+       ensure_eventmachine("evma_watch_filename");
+       return EventMachine->WatchFile(fname);
+}
+
+/*********************
+evma_unwatch_filename
+*********************/
+
+extern "C" void evma_unwatch_filename (const unsigned long sig)
+{
+       ensure_eventmachine("evma_unwatch_file");
+       EventMachine->UnwatchFile(sig);
+}
+
+/**************
+evma_watch_pid
+**************/
+
+extern "C" const unsigned long evma_watch_pid (int pid)
+{
+       ensure_eventmachine("evma_watch_pid");
+       return EventMachine->WatchPid(pid);
+}
+
+/****************
+evma_unwatch_pid
+****************/
+
+extern "C" void evma_unwatch_pid (const unsigned long sig)
+{
+       ensure_eventmachine("evma_unwatch_pid");
+       EventMachine->UnwatchPid(sig);
+}
+
+/****************************
+evma_send_data_to_connection
+****************************/
+
+extern "C" int evma_send_data_to_connection (const unsigned long binding, const char *data, int data_length)
+{
+       ensure_eventmachine("evma_send_data_to_connection");
+       return ConnectionDescriptor::SendDataToConnection (binding, data, data_length);
+}
+
+/******************
+evma_send_datagram
+******************/
+
+extern "C" int evma_send_datagram (const unsigned long binding, const char *data, int data_length, const char *address, int port)
+{
+       ensure_eventmachine("evma_send_datagram");
+       return DatagramDescriptor::SendDatagram (binding, data, data_length, address, port);
+}
+
+
+/*********************
+evma_close_connection
+*********************/
+
+extern "C" void evma_close_connection (const unsigned long binding, int after_writing)
+{
+       ensure_eventmachine("evma_close_connection");
+       ConnectionDescriptor::CloseConnection (binding, (after_writing ? true : false));
+}
+
+/***********************************
+evma_report_connection_error_status
+***********************************/
+
+extern "C" int evma_report_connection_error_status (const unsigned long binding)
+{
+       ensure_eventmachine("evma_report_connection_error_status");
+       return ConnectionDescriptor::ReportErrorStatus (binding);
+}
+
+/********************
+evma_stop_tcp_server
+********************/
+
+extern "C" void evma_stop_tcp_server (const unsigned long binding)
+{
+       ensure_eventmachine("evma_stop_tcp_server");
+       AcceptorDescriptor::StopAcceptor (binding);
+}
+
+
+/*****************
+evma_stop_machine
+*****************/
+
+extern "C" void evma_stop_machine()
+{
+       ensure_eventmachine("evma_stop_machine");
+       EventMachine->ScheduleHalt();
+}
+
+
+/**************
+evma_start_tls
+**************/
+
+extern "C" void evma_start_tls (const unsigned long binding)
+{
+       ensure_eventmachine("evma_start_tls");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               ed->StartTls();
+}
+
+/******************
+evma_set_tls_parms
+******************/
+
+extern "C" void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer)
+{
+       ensure_eventmachine("evma_set_tls_parms");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false));
+}
+
+/******************
+evma_get_peer_cert
+******************/
+
+#ifdef WITH_SSL
+extern "C" X509 *evma_get_peer_cert (const unsigned long binding)
+{
+       ensure_eventmachine("evma_get_peer_cert");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               return ed->GetPeerCert();
+       return NULL;
+}
+#endif
+
+/********************
+evma_accept_ssl_peer
+********************/
+
+#ifdef WITH_SSL
+extern "C" void evma_accept_ssl_peer (const unsigned long binding)
+{
+       ensure_eventmachine("evma_accept_ssl_peer");
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               cd->AcceptSslPeer();
+}
+#endif
+
+/*****************
+evma_get_peername
+*****************/
+
+extern "C" int evma_get_peername (const unsigned long binding, struct sockaddr *sa)
+{
+       ensure_eventmachine("evma_get_peername");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->GetPeername (sa) ? 1 : 0;
+       }
+       else
+               return 0;
+}
+
+/*****************
+evma_get_sockname
+*****************/
+
+extern "C" int evma_get_sockname (const unsigned long binding, struct sockaddr *sa)
+{
+       ensure_eventmachine("evma_get_sockname");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->GetSockname (sa) ? 1 : 0;
+       }
+       else
+               return 0;
+}
+
+/***********************
+evma_get_subprocess_pid
+***********************/
+
+extern "C" int evma_get_subprocess_pid (const unsigned long binding, pid_t *pid)
+{
+       ensure_eventmachine("evma_get_subprocess_pid");
+       #ifdef OS_UNIX
+       PipeDescriptor *pd = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
+       if (pd) {
+               return pd->GetSubprocessPid (pid) ? 1 : 0;
+       }
+       else if (pid && EventMachine->SubprocessPid) {
+               *pid = EventMachine->SubprocessPid;
+               return 1;
+       }
+       else
+               return 0;
+       #else
+       return 0;
+       #endif
+}
+
+/**************************
+evma_get_subprocess_status
+**************************/
+
+extern "C" int evma_get_subprocess_status (const unsigned long binding, int *status)
+{
+       ensure_eventmachine("evma_get_subprocess_status");
+       if (status) {
+               *status = EventMachine->SubprocessExitStatus;
+               return 1;
+       }
+       else
+               return 0;
+}
+
+/*************************
+evma_get_connection_count
+*************************/
+
+extern "C" int evma_get_connection_count()
+{
+       ensure_eventmachine("evma_get_connection_count");
+       return EventMachine->GetConnectionCount();
+}
+
+/*********************
+evma_signal_loopbreak
+*********************/
+
+extern "C" void evma_signal_loopbreak()
+{
+       ensure_eventmachine("evma_signal_loopbreak");
+       EventMachine->SignalLoopBreaker();
+}
+
+
+
+/****************
+evma__write_file
+****************/
+
+extern "C" const unsigned long evma__write_file (const char *filename)
+{
+       ensure_eventmachine("evma__write_file");
+       return EventMachine->_OpenFileForWriting (filename);
+}
+
+
+/********************************
+evma_get_comm_inactivity_timeout
+********************************/
+
+extern "C" float evma_get_comm_inactivity_timeout (const unsigned long binding)
+{
+       ensure_eventmachine("evma_get_comm_inactivity_timeout");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->GetCommInactivityTimeout();
+       }
+       else
+               return 0.0; //Perhaps this should be an exception. Access to an unknown binding.
+}
+
+/********************************
+evma_set_comm_inactivity_timeout
+********************************/
+
+extern "C" int evma_set_comm_inactivity_timeout (const unsigned long binding, float value)
+{
+       ensure_eventmachine("evma_set_comm_inactivity_timeout");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->SetCommInactivityTimeout (value);
+       }
+       else
+               return 0; //Perhaps this should be an exception. Access to an unknown binding.
+}
+
+
+/********************************
+evma_get_pending_connect_timeout
+********************************/
+
+extern "C" float evma_get_pending_connect_timeout (const unsigned long binding)
+{
+       ensure_eventmachine("evma_get_pending_connect_timeout");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->GetPendingConnectTimeout();
+       }
+       else
+               return 0.0;
+}
+
+
+/********************************
+evma_set_pending_connect_timeout
+********************************/
+
+extern "C" int evma_set_pending_connect_timeout (const unsigned long binding, float value)
+{
+       ensure_eventmachine("evma_set_pending_connect_timeout");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed) {
+               return ed->SetPendingConnectTimeout (value);
+       }
+       else
+               return 0;
+}
+
+
+/**********************
+evma_set_timer_quantum
+**********************/
+
+extern "C" void evma_set_timer_quantum (int interval)
+{
+       ensure_eventmachine("evma_set_timer_quantum");
+       EventMachine->SetTimerQuantum (interval);
+}
+
+
+/************************
+evma_get_max_timer_count
+************************/
+
+extern "C" int evma_get_max_timer_count()
+{
+       return EventMachine_t::GetMaxTimerCount();
+}
+
+
+/************************
+evma_set_max_timer_count
+************************/
+
+extern "C" void evma_set_max_timer_count (int ct)
+{
+       // This may only be called if the reactor is not running.
+
+       if (EventMachine)
+               #ifdef BUILD_FOR_RUBY
+                       rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_set_max_timer_count");
+               #else
+                       throw std::runtime_error ("eventmachine already initialized: evma_set_max_timer_count");
+               #endif
+       EventMachine_t::SetMaxTimerCount (ct);
+}
+
+/******************
+evma_setuid_string
+******************/
+
+extern "C" void evma_setuid_string (const char *username)
+{
+       // We do NOT need to be running an EM instance because this method is static.
+       EventMachine_t::SetuidString (username);
+}
+
+
+/**********
+evma_popen
+**********/
+
+extern "C" const unsigned long evma_popen (char * const*cmd_strings)
+{
+       ensure_eventmachine("evma_popen");
+       return EventMachine->Socketpair (cmd_strings);
+}
+
+
+/***************************
+evma_get_outbound_data_size
+***************************/
+
+extern "C" int evma_get_outbound_data_size (const unsigned long binding)
+{
+       ensure_eventmachine("evma_get_outbound_data_size");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       return ed ? ed->GetOutboundDataSize() : 0;
+}
+
+
+/**************
+evma_set_epoll
+**************/
+
+extern "C" void evma_set_epoll (int use)
+{
+       bUseEpoll = !!use;
+}
+
+/***************
+evma_set_kqueue
+***************/
+
+extern "C" void evma_set_kqueue (int use)
+{
+       bUseKqueue = !!use;
+}
+
+
+/**********************
+evma_set_rlimit_nofile
+**********************/
+
+extern "C" int evma_set_rlimit_nofile (int nofiles)
+{
+       return EventMachine_t::SetRlimitNofile (nofiles);
+}
+
+
+/*********************************
+evma_send_file_data_to_connection
+*********************************/
+
+extern "C" int evma_send_file_data_to_connection (const unsigned long binding, const char *filename)
+{
+       /* This is a sugaring over send_data_to_connection that reads a file into a
+        * locally-allocated buffer, and sends the file data to the remote peer.
+        * Return the number of bytes written to the caller.
+        * TODO, needs to impose a limit on the file size. This is intended only for
+        * small files. (I don't know, maybe 8K or less.) For larger files, use interleaved
+        * I/O to avoid slowing the rest of the system down.
+        * TODO: we should return a code rather than barf, in case of file-not-found.
+        * TODO, does this compile on Windows?
+        * TODO, given that we want this to work only with small files, how about allocating
+        * the buffer on the stack rather than the heap?
+        *
+        * Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive
+        * errno in case of other errors.
+        *
+        * Contributed by Kirk Haines.
+        */
+
+       char data[32*1024];
+       int r;
+
+       ensure_eventmachine("evma_send_file_data_to_connection");
+
+       int Fd = open (filename, O_RDONLY);
+
+       if (Fd < 0)
+               return errno;
+       // From here on, all early returns MUST close Fd.
+
+       struct stat st;
+       if (fstat (Fd, &st)) {
+               int e = errno;
+               close (Fd);
+               return e;
+       }
+
+       off_t filesize = st.st_size;
+       if (filesize <= 0) {
+               close (Fd);
+               return 0;
+       }
+       else if (filesize > (off_t) sizeof(data)) {
+               close (Fd);
+               return -1;
+       }
+
+
+       r = read (Fd, data, filesize);
+       if (r != filesize) {
+               int e = errno;
+               close (Fd);
+               return e;
+       }
+       evma_send_data_to_connection (binding, data, r);
+       close (Fd);
+
+       return 0;
+}
+
+
+/****************
+evma_start_proxy
+*****************/
+
+extern "C" void evma_start_proxy (const unsigned long from, const unsigned long to, const unsigned long bufsize)
+{
+       ensure_eventmachine("evma_start_proxy");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+       if (ed)
+               ed->StartProxy(to, bufsize);
+}
+
+
+/***************
+evma_stop_proxy
+****************/
+
+extern "C" void evma_stop_proxy (const unsigned long from)
+{
+       ensure_eventmachine("evma_stop_proxy");
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+       if (ed)
+               ed->StopProxy();
+}
+
+
+/***************************
+evma_get_heartbeat_interval
+****************************/
+
+extern "C" float evma_get_heartbeat_interval()
+{
+       ensure_eventmachine("evma_get_heartbeat_interval");
+       return EventMachine->GetHeartbeatInterval();
+}
+
+
+/***************************
+evma_set_heartbeat_interval
+****************************/
+
+extern "C" int evma_set_heartbeat_interval(float interval)
+{
+       ensure_eventmachine("evma_set_heartbeat_interval");
+       return EventMachine->SetHeartbeatInterval(interval);
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cplusplus.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/cplusplus.cpp
new file mode 100644 (file)
index 0000000..1c8bf22
--- /dev/null
@@ -0,0 +1,202 @@
+/*****************************************************************************
+
+$Id$
+
+File:     cplusplus.cpp
+Date:     27Jul07
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+#include "project.h"
+
+
+namespace EM {
+       static map<unsigned long, Eventable*> Eventables;
+       static map<unsigned long, void(*)()> Timers;
+}
+
+
+/*******
+EM::Run
+*******/
+
+void EM::Run (void (*start_func)())
+{
+       evma_set_epoll (1);
+       evma_initialize_library (EM::Callback);
+       if (start_func)
+               AddTimer (0, start_func);
+       evma_run_machine();
+       evma_release_library();
+}
+
+/************
+EM::AddTimer
+************/
+
+void EM::AddTimer (int milliseconds, void (*func)())
+{
+       if (func) {
+               const unsigned long sig = evma_install_oneshot_timer (milliseconds);
+               #ifndef HAVE_MAKE_PAIR
+               Timers.insert (map<unsigned long, void(*)()>::value_type (sig, func));
+               #else
+               Timers.insert (make_pair (sig, func));
+               #endif
+       }
+}
+
+
+/***************
+EM::StopReactor
+***************/
+
+void EM::StopReactor()
+{
+       evma_stop_machine();
+}
+
+
+/********************
+EM::Acceptor::Accept
+********************/
+
+void EM::Acceptor::Accept (const unsigned long signature)
+{
+       Connection *c = MakeConnection();
+       c->Signature = signature;
+       #ifndef HAVE_MAKE_PAIR
+       Eventables.insert (std::map<unsigned long,EM::Eventable*>::value_type (c->Signature, c));
+       #else
+       Eventables.insert (make_pair (c->Signature, c));
+       #endif
+       c->PostInit();
+}
+
+/************************
+EM::Connection::SendData
+************************/
+
+void EM::Connection::SendData (const char *data)
+{
+       if (data)
+               SendData (data, strlen (data));
+}
+
+
+/************************
+EM::Connection::SendData
+************************/
+
+void EM::Connection::SendData (const char *data, int length)
+{
+       evma_send_data_to_connection (Signature, data, length);
+}
+
+
+/*********************
+EM::Connection::Close
+*********************/
+
+void EM::Connection::Close (bool afterWriting)
+{
+       evma_close_connection (Signature, afterWriting);
+}
+
+
+/***************************
+EM::Connection::BindConnect
+***************************/
+
+void EM::Connection::BindConnect (const char *bind_addr, int bind_port, const char *host, int port)
+{
+       Signature = evma_connect_to_server (bind_addr, bind_port, host, port);
+       #ifndef HAVE_MAKE_PAIR
+       Eventables.insert (std::map<unsigned long,EM::Eventable*>::value_type (Signature, this));
+       #else
+       Eventables.insert (make_pair (Signature, this));
+       #endif
+}
+
+/***********************
+EM::Connection::Connect
+***********************/
+
+void EM::Connection::Connect (const char *host, int port)
+{
+       this->BindConnect(NULL, 0, host, port);
+}
+
+/*******************
+EM::Acceptor::Start
+*******************/
+
+void EM::Acceptor::Start (const char *host, int port)
+{
+       Signature = evma_create_tcp_server (host, port);
+       #ifndef HAVE_MAKE_PAIR
+       Eventables.insert (std::map<unsigned long,EM::Eventable*>::value_type (Signature, this));
+       #else
+       Eventables.insert (make_pair (Signature, this));
+       #endif
+}
+
+
+
+/************
+EM::Callback
+************/
+
+void EM::Callback (const unsigned long sig, int ev, const char *data, const unsigned long length)
+{
+       EM::Eventable *e;
+       void (*f)();
+
+       switch (ev) {
+               case EM_TIMER_FIRED:
+                       f = Timers [length]; // actually a binding
+                       if (f)
+                               (*f)();
+                       Timers.erase (length);
+                       break;
+
+               case EM_CONNECTION_READ:
+                       e = EM::Eventables [sig];
+                       e->ReceiveData (data, length);
+                       break;
+
+               case EM_CONNECTION_COMPLETED:
+                       e = EM::Eventables [sig];
+                       e->ConnectionCompleted();
+                       break;
+
+               case EM_CONNECTION_ACCEPTED:
+                       e = EM::Eventables [sig];
+                       e->Accept (length); // actually a binding
+                       break;
+
+               case EM_CONNECTION_UNBOUND:
+                       e = EM::Eventables [sig];
+                       e->Unbind();
+                       EM::Eventables.erase (sig);
+                       delete e;
+                       break;
+
+               case EM_SSL_HANDSHAKE_COMPLETED:
+                       e = EM::Eventables [sig];
+                       e->SslHandshakeCompleted();
+                       break;
+       }
+}
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.cpp
new file mode 100644 (file)
index 0000000..09ff528
--- /dev/null
@@ -0,0 +1,1893 @@
+/*****************************************************************************
+
+$Id$
+
+File:     ed.cpp
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+
+/********************
+SetSocketNonblocking
+********************/
+
+bool SetSocketNonblocking (SOCKET sd)
+{
+       #ifdef OS_UNIX
+       int val = fcntl (sd, F_GETFL, 0);
+       return (fcntl (sd, F_SETFL, val | O_NONBLOCK) != SOCKET_ERROR) ? true : false;
+       #endif
+       
+       #ifdef OS_WIN32
+       #ifdef BUILD_FOR_RUBY
+       // 14Jun09 Ruby provides its own wrappers for ioctlsocket. On 1.8 this is a simple wrapper,
+       // however, 1.9 keeps its own state about the socket.
+       // NOTE: F_GETFL is not supported
+       return (fcntl (sd, F_SETFL, O_NONBLOCK) == 0) ? true : false;
+       #else
+       unsigned long one = 1;
+       return (ioctlsocket (sd, FIONBIO, &one) == 0) ? true : false;
+       #endif
+       #endif
+}
+
+
+/****************************************
+EventableDescriptor::EventableDescriptor
+****************************************/
+
+EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em):
+       bCloseNow (false),
+       bCloseAfterWriting (false),
+       MySocket (sd),
+       EventCallback (NULL),
+       bCallbackUnbind (true),
+       UnbindReasonCode (0),
+       ProxyTarget(NULL),
+       ProxiedFrom(NULL),
+       MaxOutboundBufSize(0),
+       MyEventMachine (em),
+       PendingConnectTimeout(20000000)
+{
+       /* There are three ways to close a socket, all of which should
+        * automatically signal to the event machine that this object
+        * should be removed from the polling scheduler.
+        * First is a hard close, intended for bad errors or possible
+        * security violations. It immediately closes the connection
+        * and puts this object into an error state.
+        * Second is to set bCloseNow, which will cause the event machine
+        * to delete this object (and thus close the connection in our
+        * destructor) the next chance it gets. bCloseNow also inhibits
+        * the writing of new data on the socket (but not necessarily
+        * the reading of new data).
+        * The third way is to set bCloseAfterWriting, which inhibits
+        * the writing of new data and converts to bCloseNow as soon
+        * as everything in the outbound queue has been written.
+        * bCloseAfterWriting is really for use only by protocol handlers
+        * (for example, HTTP writes an HTML page and then closes the
+        * connection). All of the error states we generate internally
+        * cause an immediate close to be scheduled, which may have the
+        * effect of discarding outbound data.
+        */
+
+       if (sd == INVALID_SOCKET)
+               throw std::runtime_error ("bad eventable descriptor");
+       if (MyEventMachine == NULL)
+               throw std::runtime_error ("bad em in eventable descriptor");
+       CreatedAt = gCurrentLoopTime;
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = 0;
+       EpollEvent.data.ptr = this;
+       #endif
+}
+
+
+/*****************************************
+EventableDescriptor::~EventableDescriptor
+*****************************************/
+
+EventableDescriptor::~EventableDescriptor()
+{
+       if (EventCallback && bCallbackUnbind)
+               (*EventCallback)(GetBinding(), EM_CONNECTION_UNBOUND, NULL, UnbindReasonCode);
+       if (ProxiedFrom) {
+               (*EventCallback)(ProxiedFrom->GetBinding(), EM_PROXY_TARGET_UNBOUND, NULL, 0);
+               ProxiedFrom->StopProxy();
+       }
+       StopProxy();
+       Close();
+}
+
+
+/*************************************
+EventableDescriptor::SetEventCallback
+*************************************/
+
+void EventableDescriptor::SetEventCallback (void(*cb)(const unsigned long, int, const char*, const unsigned long))
+{
+       EventCallback = cb;
+}
+
+
+/**************************
+EventableDescriptor::Close
+**************************/
+
+void EventableDescriptor::Close()
+{
+       // Close the socket right now. Intended for emergencies.
+       if (MySocket != INVALID_SOCKET) {
+               shutdown (MySocket, 1);
+               closesocket (MySocket);
+               MySocket = INVALID_SOCKET;
+       }
+}
+
+
+/*********************************
+EventableDescriptor::ShouldDelete
+*********************************/
+
+bool EventableDescriptor::ShouldDelete()
+{
+       /* For use by a socket manager, which needs to know if this object
+        * should be removed from scheduling events and deleted.
+        * Has an immediate close been scheduled, or are we already closed?
+        * If either of these are the case, return true. In theory, the manager will
+        * then delete us, which in turn will make sure the socket is closed.
+        * Note, if bCloseAfterWriting is true, we check a virtual method to see
+        * if there is outbound data to write, and only request a close if there is none.
+        */
+
+       return ((MySocket == INVALID_SOCKET) || bCloseNow || (bCloseAfterWriting && (GetOutboundDataSize() <= 0)));
+}
+
+
+/**********************************
+EventableDescriptor::ScheduleClose
+**********************************/
+
+void EventableDescriptor::ScheduleClose (bool after_writing)
+{
+       // KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled.
+       if (after_writing)
+               bCloseAfterWriting = true;
+       else
+               bCloseNow = true;
+}
+
+
+/*************************************
+EventableDescriptor::IsCloseScheduled
+*************************************/
+
+bool EventableDescriptor::IsCloseScheduled()
+{
+       // KEEP THIS SYNCHRONIZED WITH ::ScheduleClose.
+       return (bCloseNow || bCloseAfterWriting);
+}
+
+
+/*******************************
+EventableDescriptor::StartProxy
+*******************************/
+
+void EventableDescriptor::StartProxy(const unsigned long to, const unsigned long bufsize)
+{
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (to));
+       if (ed) {
+               StopProxy();
+               ProxyTarget = ed;
+               ed->SetProxiedFrom(this, bufsize);
+               return;
+       }
+       throw std::runtime_error ("Tried to proxy to an invalid descriptor");
+}
+
+
+/******************************
+EventableDescriptor::StopProxy
+******************************/
+
+void EventableDescriptor::StopProxy()
+{
+       if (ProxyTarget) {
+               ProxyTarget->SetProxiedFrom(NULL, 0);
+               ProxyTarget = NULL;
+       }
+}
+
+
+/***********************************
+EventableDescriptor::SetProxiedFrom
+***********************************/
+
+void EventableDescriptor::SetProxiedFrom(EventableDescriptor *from, const unsigned long bufsize)
+{
+       ProxiedFrom = from;
+       MaxOutboundBufSize = bufsize;
+}
+
+
+/********************************************
+EventableDescriptor::_GenericInboundDispatch
+********************************************/
+
+void EventableDescriptor::_GenericInboundDispatch(const char *buf, int size)
+{
+       assert(EventCallback);
+
+       if (ProxyTarget)
+               ProxyTarget->SendOutboundData(buf, size);
+       else
+               (*EventCallback)(GetBinding(), EM_CONNECTION_READ, buf, size);
+}
+
+
+/*********************************************
+EventableDescriptor::GetPendingConnectTimeout
+*********************************************/
+
+float EventableDescriptor::GetPendingConnectTimeout()
+{
+       return ((float)PendingConnectTimeout / 1000000);
+}
+
+
+/*********************************************
+EventableDescriptor::SetPendingConnectTimeout
+*********************************************/
+
+int EventableDescriptor::SetPendingConnectTimeout (float value)
+{
+       if (value > 0) {
+               PendingConnectTimeout = (Int64)(value * 1000000);
+               return 1;
+       }
+       return 0;
+}
+
+
+/******************************************
+ConnectionDescriptor::ConnectionDescriptor
+******************************************/
+
+ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
+       EventableDescriptor (sd, em),
+       bPaused (false),
+       bConnectPending (false),
+       bNotifyReadable (false),
+       bNotifyWritable (false),
+       bWatchOnly (false),
+       bReadAttemptedAfterClose (false),
+       bWriteAttemptedAfterClose (false),
+       OutboundDataSize (0),
+       #ifdef WITH_SSL
+       SslBox (NULL),
+       bHandshakeSignaled (false),
+       bSslVerifyPeer (false),
+       bSslPeerAccepted(false),
+       #endif
+       #ifdef HAVE_KQUEUE
+       bGotExtraKqueueEvent(false),
+       #endif
+       bIsServer (false),
+       LastIo (gCurrentLoopTime),
+       InactivityTimeout (0)
+{
+       // 22Jan09: Moved ArmKqueueWriter into SetConnectPending() to fix assertion failure in _WriteOutboundData()
+       //  5May09: Moved EPOLLOUT into SetConnectPending() so it doesn't happen for attached read pipes
+}
+
+
+/*******************************************
+ConnectionDescriptor::~ConnectionDescriptor
+*******************************************/
+
+ConnectionDescriptor::~ConnectionDescriptor()
+{
+       // Run down any stranded outbound data.
+       for (size_t i=0; i < OutboundPages.size(); i++)
+               OutboundPages[i].Free();
+
+       #ifdef WITH_SSL
+       if (SslBox)
+               delete SslBox;
+       #endif
+}
+
+
+/**************************************************
+STATIC: ConnectionDescriptor::SendDataToConnection
+**************************************************/
+
+int ConnectionDescriptor::SendDataToConnection (const unsigned long binding, const char *data, int data_length)
+{
+       // TODO: This is something of a hack, or at least it's a static method of the wrong class.
+       // TODO: Poor polymorphism here. We should be calling one virtual method
+       // instead of hacking out the runtime information of the target object.
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->SendOutboundData (data, data_length);
+       DatagramDescriptor *ds = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
+       if (ds)
+               return ds->SendOutboundData (data, data_length);
+       #ifdef OS_UNIX
+       PipeDescriptor *ps = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
+       if (ps)
+               return ps->SendOutboundData (data, data_length);
+       #endif
+       return -1;
+}
+
+
+/*********************************************
+STATIC: ConnectionDescriptor::CloseConnection
+*********************************************/
+
+void ConnectionDescriptor::CloseConnection (const unsigned long binding, bool after_writing)
+{
+       // TODO: This is something of a hack, or at least it's a static method of the wrong class.
+       EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+       if (ed)
+               ed->ScheduleClose (after_writing);
+}
+
+/***********************************************
+STATIC: ConnectionDescriptor::ReportErrorStatus
+***********************************************/
+
+int ConnectionDescriptor::ReportErrorStatus (const unsigned long binding)
+{
+       // TODO: This is something of a hack, or at least it's a static method of the wrong class.
+       // TODO: Poor polymorphism here. We should be calling one virtual method
+       // instead of hacking out the runtime information of the target object.
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+       if (cd)
+               return cd->_ReportErrorStatus();
+       return -1;
+}
+
+/***********************************
+ConnectionDescriptor::_UpdateEvents
+************************************/
+
+void ConnectionDescriptor::_UpdateEvents()
+{
+       _UpdateEvents(true, true);
+}
+
+void ConnectionDescriptor::_UpdateEvents(bool read, bool write)
+{
+       if (MySocket == INVALID_SOCKET)
+               return;
+
+       #ifdef HAVE_EPOLL
+       unsigned int old = EpollEvent.events;
+
+       if (read) {
+               if (SelectForRead())
+                       EpollEvent.events |= EPOLLIN;
+               else
+                       EpollEvent.events &= ~EPOLLIN;
+       }
+
+       if (write) {
+               if (SelectForWrite())
+                       EpollEvent.events |= EPOLLOUT;
+               else
+                       EpollEvent.events &= ~EPOLLOUT;
+       }
+
+       if (old != EpollEvent.events)
+               MyEventMachine->Modify (this);
+       #endif
+
+       #ifdef HAVE_KQUEUE
+       if (read && SelectForRead())
+               MyEventMachine->ArmKqueueReader (this);
+       if (write && SelectForWrite())
+               MyEventMachine->ArmKqueueWriter (this);
+       #endif
+}
+
+/***************************************
+ConnectionDescriptor::SetConnectPending
+****************************************/
+
+void ConnectionDescriptor::SetConnectPending(bool f)
+{
+       bConnectPending = f;
+       _UpdateEvents();
+}
+
+
+/**********************************
+ConnectionDescriptor::SetWatchOnly
+***********************************/
+
+void ConnectionDescriptor::SetWatchOnly(bool watching)
+{
+       bWatchOnly = watching;
+       _UpdateEvents();
+}
+
+
+/*********************************
+ConnectionDescriptor::HandleError
+*********************************/
+
+void ConnectionDescriptor::HandleError()
+{
+       if (bWatchOnly) {
+               // An EPOLLHUP | EPOLLIN condition will call Read() before HandleError(), in which case the
+               // socket is already detached and invalid, so we don't need to do anything.
+               if (MySocket == INVALID_SOCKET) return;
+
+               // HandleError() is called on WatchOnly descriptors by the epoll reactor
+               // when it gets a EPOLLERR | EPOLLHUP. Usually this would show up as a readable and
+               // writable event on other reactors, so we have to fire those events ourselves.
+               if (bNotifyReadable) Read();
+               if (bNotifyWritable) Write();
+       } else {
+               ScheduleClose (false);
+       }
+}
+
+
+/***********************************
+ConnectionDescriptor::ScheduleClose
+***********************************/
+
+void ConnectionDescriptor::ScheduleClose (bool after_writing)
+{
+       if (bWatchOnly)
+               throw std::runtime_error ("cannot close 'watch only' connections");
+
+       EventableDescriptor::ScheduleClose(after_writing);
+}
+
+
+/***************************************
+ConnectionDescriptor::SetNotifyReadable
+****************************************/
+
+void ConnectionDescriptor::SetNotifyReadable(bool readable)
+{
+       if (!bWatchOnly)
+               throw std::runtime_error ("notify_readable must be on 'watch only' connections");
+
+       bNotifyReadable = readable;
+       _UpdateEvents(true, false);
+}
+
+
+/***************************************
+ConnectionDescriptor::SetNotifyWritable
+****************************************/
+
+void ConnectionDescriptor::SetNotifyWritable(bool writable)
+{
+       if (!bWatchOnly)
+               throw std::runtime_error ("notify_writable must be on 'watch only' connections");
+
+       bNotifyWritable = writable;
+       _UpdateEvents(false, true);
+}
+
+
+/**************************************
+ConnectionDescriptor::SendOutboundData
+**************************************/
+
+int ConnectionDescriptor::SendOutboundData (const char *data, int length)
+{
+       if (bWatchOnly)
+               throw std::runtime_error ("cannot send data on a 'watch only' connection");
+
+       if (ProxiedFrom && MaxOutboundBufSize && GetOutboundDataSize() + length > MaxOutboundBufSize)
+               ProxiedFrom->Pause();
+
+       #ifdef WITH_SSL
+       if (SslBox) {
+               if (length > 0) {
+                       int w = SslBox->PutPlaintext (data, length);
+                       if (w < 0)
+                               ScheduleClose (false);
+                       else
+                               _DispatchCiphertext();
+               }
+               // TODO: What's the correct return value?
+               return 1; // That's a wild guess, almost certainly wrong.
+       }
+       else
+       #endif
+               return _SendRawOutboundData (data, length);
+}
+
+
+
+/******************************************
+ConnectionDescriptor::_SendRawOutboundData
+******************************************/
+
+int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
+{
+       /* This internal method is called to schedule bytes that
+        * will be sent out to the remote peer.
+        * It's not directly accessed by the caller, who hits ::SendOutboundData,
+        * which may or may not filter or encrypt the caller's data before
+        * sending it here.
+        */
+
+       // Highly naive and incomplete implementation.
+       // There's no throttle for runaways (which should abort only this connection
+       // and not the whole process), and no coalescing of small pages.
+       // (Well, not so bad, small pages are coalesced in ::Write)
+
+       if (IsCloseScheduled())
+       //if (bCloseNow || bCloseAfterWriting)
+               return 0;
+
+       if (!data && (length > 0))
+               throw std::runtime_error ("bad outbound data");
+       char *buffer = (char *) malloc (length + 1);
+       if (!buffer)
+               throw std::runtime_error ("no allocation for outbound data");
+
+       memcpy (buffer, data, length);
+       buffer [length] = 0;
+       OutboundPages.push_back (OutboundPage (buffer, length));
+       OutboundDataSize += length;
+
+       _UpdateEvents(false, true);
+
+       return length;
+}
+
+
+
+/***********************************
+ConnectionDescriptor::SelectForRead
+***********************************/
+
+bool ConnectionDescriptor::SelectForRead()
+{
+  /* A connection descriptor is always scheduled for read,
+   * UNLESS it's in a pending-connect state.
+   * On Linux, unlike Unix, a nonblocking socket on which
+   * connect has been called, does NOT necessarily select
+   * both readable and writable in case of error.
+   * The socket will select writable when the disposition
+   * of the connect is known. On the other hand, a socket
+   * which successfully connects and selects writable may
+   * indeed have some data available on it, so it will
+   * select readable in that case, violating expectations!
+   * So we will not poll for readability until the socket
+   * is known to be in a connected state.
+   */
+
+  if (bPaused)
+    return false;
+  else if (bConnectPending)
+    return false;
+  else if (bWatchOnly)
+    return bNotifyReadable ? true : false;
+  else
+    return true;
+}
+
+
+/************************************
+ConnectionDescriptor::SelectForWrite
+************************************/
+
+bool ConnectionDescriptor::SelectForWrite()
+{
+  /* Cf the notes under SelectForRead.
+   * In a pending-connect state, we ALWAYS select for writable.
+   * In a normal state, we only select for writable when we
+   * have outgoing data to send.
+   */
+
+  if (bPaused)
+    return false;
+  else if (bConnectPending)
+    return true;
+  else if (bWatchOnly)
+    return bNotifyWritable ? true : false;
+  else
+    return (GetOutboundDataSize() > 0);
+}
+
+/***************************
+ConnectionDescriptor::Pause
+***************************/
+
+bool ConnectionDescriptor::Pause()
+{
+       if (bWatchOnly)
+               throw std::runtime_error ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+
+       bool old = bPaused;
+       bPaused = true;
+       _UpdateEvents();
+       return old == false;
+}
+
+/****************************
+ConnectionDescriptor::Resume
+****************************/
+
+bool ConnectionDescriptor::Resume()
+{
+       if (bWatchOnly)
+               throw std::runtime_error ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+
+       bool old = bPaused;
+       bPaused = false;
+       _UpdateEvents();
+       return old == true;
+}
+
+/**************************
+ConnectionDescriptor::Read
+**************************/
+
+void ConnectionDescriptor::Read()
+{
+       /* Read and dispatch data on a socket that has selected readable.
+        * It's theoretically possible to get and dispatch incoming data on
+        * a socket that has already been scheduled for closing or close-after-writing.
+        * In those cases, we'll leave it up the to protocol handler to "do the
+        * right thing" (which probably means to ignore the incoming data).
+        *
+        * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come
+        * here with the socket already closed, after the process receives
+        * a ctrl-C signal (not sure if that's TERM or INT on BSD). The application
+        * was one in which network connections were doing a lot of interleaved reads
+        * and writes.
+        * Since we always write before reading (in order to keep the outbound queues
+        * as light as possible), I think what happened is that an interrupt caused
+        * the socket to be closed in ConnectionDescriptor::Write. We'll then
+        * come here in the same pass through the main event loop, and won't get
+        * cleaned up until immediately after.
+        * We originally asserted that the socket was valid when we got here.
+        * To deal properly with the possibility that we are closed when we get here,
+        * I removed the assert. HOWEVER, the potential for an infinite loop scares me,
+        * so even though this is really clunky, I added a flag to assert that we never
+        * come here more than once after being closed. (FCianfrocca)
+        */
+
+       int sd = GetSocket();
+       //assert (sd != INVALID_SOCKET); (original, removed 22Aug06)
+       if (sd == INVALID_SOCKET) {
+               assert (!bReadAttemptedAfterClose);
+               bReadAttemptedAfterClose = true;
+               return;
+       }
+
+       if (bWatchOnly) {
+               if (bNotifyReadable && EventCallback)
+                       (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_READABLE, NULL, 0);
+               return;
+       }
+
+       LastIo = gCurrentLoopTime;
+
+       int total_bytes_read = 0;
+       char readbuffer [16 * 1024 + 1];
+
+       for (int i=0; i < 10; i++) {
+               // Don't read just one buffer and then move on. This is faster
+               // if there is a lot of incoming.
+               // But don't read indefinitely. Give other sockets a chance to run.
+               // NOTICE, we're reading one less than the buffer size.
+               // That's so we can put a guard byte at the end of what we send
+               // to user code.
+               
+
+               int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
+               //cerr << "<R:" << r << ">";
+
+               if (r > 0) {
+                       total_bytes_read += r;
+
+                       // Add a null-terminator at the the end of the buffer
+                       // that we will send to the callback.
+                       // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+                       // to be able to depend on this behavior, so they will have
+                       // the option to do some things faster. Additionally it's
+                       // a security guard against buffer overflows.
+                       readbuffer [r] = 0;
+                       _DispatchInboundData (readbuffer, r);
+               }
+               else if (r == 0) {
+                       break;
+               }
+               else {
+                       // Basically a would-block, meaning we've read everything there is to read.
+                       break;
+               }
+
+       }
+
+
+       if (total_bytes_read == 0) {
+               // If we read no data on a socket that selected readable,
+               // it generally means the other end closed the connection gracefully.
+               ScheduleClose (false);
+               //bCloseNow = true;
+       }
+
+}
+
+
+
+/******************************************
+ConnectionDescriptor::_DispatchInboundData
+******************************************/
+
+void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
+{
+       #ifdef WITH_SSL
+       if (SslBox) {
+               SslBox->PutCiphertext (buffer, size);
+
+               int s;
+               char B [2048];
+               while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) {
+                       _CheckHandshakeStatus();
+                       B [s] = 0;
+                       _GenericInboundDispatch(B, s);
+               }
+
+               // If our SSL handshake had a problem, shut down the connection.
+               if (s == -2) {
+                       ScheduleClose(false);
+                       return;
+               }
+
+               _CheckHandshakeStatus();
+               _DispatchCiphertext();
+       }
+       else {
+               _GenericInboundDispatch(buffer, size);
+       }
+       #endif
+
+       #ifdef WITHOUT_SSL
+       _GenericInboundDispatch(buffer, size);
+       #endif
+}
+
+
+
+/*******************************************
+ConnectionDescriptor::_CheckHandshakeStatus
+*******************************************/
+
+void ConnectionDescriptor::_CheckHandshakeStatus()
+{
+       #ifdef WITH_SSL
+       if (SslBox && (!bHandshakeSignaled) && SslBox->IsHandshakeCompleted()) {
+               bHandshakeSignaled = true;
+               if (EventCallback)
+                       (*EventCallback)(GetBinding(), EM_SSL_HANDSHAKE_COMPLETED, NULL, 0);
+       }
+       #endif
+}
+
+
+
+/***************************
+ConnectionDescriptor::Write
+***************************/
+
+void ConnectionDescriptor::Write()
+{
+       /* A socket which is in a pending-connect state will select
+        * writable when the disposition of the connect is known.
+        * At that point, check to be sure there are no errors,
+        * and if none, then promote the socket out of the pending
+        * state.
+        * TODO: I haven't figured out how Windows signals errors on
+        * unconnected sockets. Maybe it does the untraditional but
+        * logical thing and makes the socket selectable for error.
+        * If so, it's unsupported here for the time being, and connect
+        * errors will have to be caught by the timeout mechanism.
+        */
+
+       if (bConnectPending) {
+               int error;
+               socklen_t len;
+               len = sizeof(error);
+               #ifdef OS_UNIX
+               int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
+               #endif
+               #ifdef OS_WIN32
+               int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+               #endif
+               if ((o == 0) && (error == 0)) {
+                       if (EventCallback)
+                               (*EventCallback)(GetBinding(), EM_CONNECTION_COMPLETED, "", 0);
+
+                       // 5May09: Moved epoll/kqueue read/write arming into SetConnectPending, so it can be called
+                       // from EventMachine_t::AttachFD as well.
+                       SetConnectPending (false);
+               }
+               else
+                       ScheduleClose (false);
+                       //bCloseNow = true;
+       }
+       else {
+
+               if (bNotifyWritable) {
+                       if (EventCallback)
+                               (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0);
+
+                       _UpdateEvents(false, true);
+                       return;
+               }
+
+               assert(!bWatchOnly);
+
+               /* 5May09: Kqueue bugs on OSX cause one extra writable event to fire even though we're using
+                  EV_ONESHOT. We ignore this extra event once, but only the first time. If it happens again,
+                  we should fall through to the assert(nbytes>0) failure to catch any EM bugs which might cause
+                  ::Write to be called in a busy-loop.
+               */
+               #ifdef HAVE_KQUEUE
+               if (MyEventMachine->UsingKqueue()) {
+                       if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) {
+                               bGotExtraKqueueEvent = true;
+                               return;
+                       } else if (OutboundDataSize > 0) {
+                               bGotExtraKqueueEvent = false;
+                       }
+               }
+               #endif
+
+               _WriteOutboundData();
+       }
+}
+
+
+/****************************************
+ConnectionDescriptor::_WriteOutboundData
+****************************************/
+
+void ConnectionDescriptor::_WriteOutboundData()
+{
+       /* This is a helper function called by ::Write.
+        * It's possible for a socket to select writable and then no longer
+        * be writable by the time we get around to writing. The kernel might
+        * have used up its available output buffers between the select call
+        * and when we get here. So this condition is not an error.
+        *
+        * 20Jul07, added the same kind of protection against an invalid socket
+        * that is at the top of ::Read. Not entirely how this could happen in 
+        * real life (connection-reset from the remote peer, perhaps?), but I'm
+        * doing it to address some reports of crashing under heavy loads.
+        */
+
+       int sd = GetSocket();
+       //assert (sd != INVALID_SOCKET);
+       if (sd == INVALID_SOCKET) {
+               assert (!bWriteAttemptedAfterClose);
+               bWriteAttemptedAfterClose = true;
+               return;
+       }
+
+       LastIo = gCurrentLoopTime;
+       size_t nbytes = 0;
+
+       #ifdef HAVE_WRITEV
+       int iovcnt = OutboundPages.size();
+       // Max of 16 outbound pages at a time
+       if (iovcnt > 16) iovcnt = 16;
+
+       struct iovec iov[ iovcnt ];
+
+       for(int i = 0; i < iovcnt; i++){
+               OutboundPage *op = &(OutboundPages[i]);
+               iov[i].iov_base = (void *)(op->Buffer + op->Offset);
+               iov[i].iov_len  = op->Length - op->Offset;
+
+               nbytes += iov[i].iov_len;
+       }
+       #else
+       char output_buffer [16 * 1024];
+
+       while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
+               OutboundPage *op = &(OutboundPages[0]);
+               if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
+                       memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
+                       nbytes += (op->Length - op->Offset);
+                       op->Free();
+                       OutboundPages.pop_front();
+               }
+               else {
+                       int len = sizeof(output_buffer) - nbytes;
+                       memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
+                       op->Offset += len;
+                       nbytes += len;
+               }
+       }
+       #endif
+
+       // We should never have gotten here if there were no data to write,
+       // so assert that as a sanity check.
+       // Don't bother to make sure nbytes is less than output_buffer because
+       // if it were we probably would have crashed already.
+       assert (nbytes > 0);
+
+       assert (GetSocket() != INVALID_SOCKET);
+       #ifdef HAVE_WRITEV
+       int bytes_written = writev (GetSocket(), iov, iovcnt);
+       #else
+       int bytes_written = write (GetSocket(), output_buffer, nbytes);
+       #endif
+
+       bool err = false;
+       if (bytes_written < 0) {
+               err = true;
+               bytes_written = 0;
+       }
+
+       assert (bytes_written >= 0);
+       OutboundDataSize -= bytes_written;
+
+       if (ProxiedFrom && MaxOutboundBufSize && GetOutboundDataSize() < MaxOutboundBufSize && ProxiedFrom->IsPaused())
+               ProxiedFrom->Resume();
+
+       #ifdef HAVE_WRITEV
+       if (!err) {
+               unsigned int sent = bytes_written;
+               deque<OutboundPage>::iterator op = OutboundPages.begin();
+
+               for (int i = 0; i < iovcnt; i++) {
+                       if (iov[i].iov_len <= sent) {
+                               // Sent this page in full, free it.
+                               op->Free();
+                               OutboundPages.pop_front();
+
+                               sent -= iov[i].iov_len;
+                       } else {
+                               // Sent part (or none) of this page, increment offset to send the remainder
+                               op->Offset += sent;
+                               break;
+                       }
+
+                       // Shouldn't be possible run out of pages before the loop ends
+                       assert(op != OutboundPages.end());
+                       *op++;
+               }
+       }
+       #else
+       if ((size_t)bytes_written < nbytes) {
+               int len = nbytes - bytes_written;
+               char *buffer = (char*) malloc (len + 1);
+               if (!buffer)
+                       throw std::runtime_error ("bad alloc throwing back data");
+               memcpy (buffer, output_buffer + bytes_written, len);
+               buffer [len] = 0;
+               OutboundPages.push_front (OutboundPage (buffer, len));
+       }
+       #endif
+
+       _UpdateEvents(false, true);
+
+       if (err) {
+               #ifdef OS_UNIX
+               if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
+               #endif
+               #ifdef OS_WIN32
+               if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
+               #endif
+                       Close();
+       }
+}
+
+
+/****************************************
+ConnectionDescriptor::_ReportErrorStatus
+****************************************/
+
+int ConnectionDescriptor::_ReportErrorStatus()
+{
+       int error;
+       socklen_t len;
+       len = sizeof(error);
+       #ifdef OS_UNIX
+       int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
+       #endif
+       #ifdef OS_WIN32
+       int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+       #endif
+       if ((o == 0) && (error == 0))
+               return 0;
+       else
+               return 1;
+}
+
+
+/******************************
+ConnectionDescriptor::StartTls
+******************************/
+
+void ConnectionDescriptor::StartTls()
+{
+       #ifdef WITH_SSL
+       if (SslBox)
+               throw std::runtime_error ("SSL/TLS already running on connection");
+
+       SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding());
+       _DispatchCiphertext();
+       #endif
+
+       #ifdef WITHOUT_SSL
+       throw std::runtime_error ("Encryption not available on this event-machine");
+       #endif
+}
+
+
+/*********************************
+ConnectionDescriptor::SetTlsParms
+*********************************/
+
+void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer)
+{
+       #ifdef WITH_SSL
+       if (SslBox)
+               throw std::runtime_error ("call SetTlsParms before calling StartTls");
+       if (privkey_filename && *privkey_filename)
+               PrivateKeyFilename = privkey_filename;
+       if (certchain_filename && *certchain_filename)
+               CertChainFilename = certchain_filename;
+       bSslVerifyPeer = verify_peer;
+       #endif
+
+       #ifdef WITHOUT_SSL
+       throw std::runtime_error ("Encryption not available on this event-machine");
+       #endif
+}
+
+
+/*********************************
+ConnectionDescriptor::GetPeerCert
+*********************************/
+
+#ifdef WITH_SSL
+X509 *ConnectionDescriptor::GetPeerCert()
+{
+       if (!SslBox)
+               throw std::runtime_error ("SSL/TLS not running on this connection");
+       return SslBox->GetPeerCert();
+}
+#endif
+
+
+/***********************************
+ConnectionDescriptor::VerifySslPeer
+***********************************/
+
+#ifdef WITH_SSL
+bool ConnectionDescriptor::VerifySslPeer(const char *cert)
+{
+       bSslPeerAccepted = false;
+
+       if (EventCallback)
+               (*EventCallback)(GetBinding(), EM_SSL_VERIFY, cert, strlen(cert));
+
+       return bSslPeerAccepted;
+}
+#endif
+
+
+/***********************************
+ConnectionDescriptor::AcceptSslPeer
+***********************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::AcceptSslPeer()
+{
+       bSslPeerAccepted = true;
+}
+#endif
+
+
+/*****************************************
+ConnectionDescriptor::_DispatchCiphertext
+*****************************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::_DispatchCiphertext()
+{
+       assert (SslBox);
+
+
+       char BigBuf [2048];
+       bool did_work;
+
+       do {
+               did_work = false;
+
+               // try to drain ciphertext
+               while (SslBox->CanGetCiphertext()) {
+                       int r = SslBox->GetCiphertext (BigBuf, sizeof(BigBuf));
+                       assert (r > 0);
+                       _SendRawOutboundData (BigBuf, r);
+                       did_work = true;
+               }
+
+               // Pump the SslBox, in case it has queued outgoing plaintext
+               // This will return >0 if data was written,
+               // 0 if no data was written, and <0 if there was a fatal error.
+               bool pump;
+               do {
+                       pump = false;
+                       int w = SslBox->PutPlaintext (NULL, 0);
+                       if (w > 0) {
+                               did_work = true;
+                               pump = true;
+                       }
+                       else if (w < 0)
+                               ScheduleClose (false);
+               } while (pump);
+
+               // try to put plaintext. INCOMPLETE, doesn't belong here?
+               // In SendOutboundData, we're spooling plaintext directly
+               // into SslBox. That may be wrong, we may need to buffer it
+               // up here! 
+               /*
+               const char *ptr;
+               int ptr_length;
+               while (OutboundPlaintext.GetPage (&ptr, &ptr_length)) {
+                       assert (ptr && (ptr_length > 0));
+                       int w = SslMachine.PutPlaintext (ptr, ptr_length);
+                       if (w > 0) {
+                               OutboundPlaintext.DiscardBytes (w);
+                               did_work = true;
+                       }
+                       else
+                               break;
+               }
+               */
+
+       } while (did_work);
+
+}
+#endif
+
+
+
+/*******************************
+ConnectionDescriptor::Heartbeat
+*******************************/
+
+void ConnectionDescriptor::Heartbeat()
+{
+       /* Only allow a certain amount of time to go by while waiting
+        * for a pending connect. If it expires, then kill the socket.
+        * For a connected socket, close it if its inactivity timer
+        * has expired.
+        */
+
+       if (bConnectPending) {
+               if ((gCurrentLoopTime - CreatedAt) >= PendingConnectTimeout)
+                       ScheduleClose (false);
+                       //bCloseNow = true;
+       }
+       else {
+               if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
+                       ScheduleClose (false);
+                       //bCloseNow = true;
+       }
+}
+
+
+/****************************************
+LoopbreakDescriptor::LoopbreakDescriptor
+****************************************/
+
+LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em):
+       EventableDescriptor (sd, parent_em)
+{
+       /* This is really bad and ugly. Change someday if possible.
+        * We have to know about an event-machine (probably the one that owns us),
+        * so we can pass newly-created connections to it.
+        */
+
+       bCallbackUnbind = false;
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueReader (this);
+       #endif
+}
+
+
+
+
+/*************************
+LoopbreakDescriptor::Read
+*************************/
+
+void LoopbreakDescriptor::Read()
+{
+       // TODO, refactor, this code is probably in the wrong place.
+       assert (MyEventMachine);
+       MyEventMachine->_ReadLoopBreaker();
+}
+
+
+/**************************
+LoopbreakDescriptor::Write
+**************************/
+
+void LoopbreakDescriptor::Write()
+{
+  // Why are we here?
+  throw std::runtime_error ("bad code path in loopbreak");
+}
+
+/**************************************
+AcceptorDescriptor::AcceptorDescriptor
+**************************************/
+
+AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
+       EventableDescriptor (sd, parent_em)
+{
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueReader (this);
+       #endif
+}
+
+
+/***************************************
+AcceptorDescriptor::~AcceptorDescriptor
+***************************************/
+
+AcceptorDescriptor::~AcceptorDescriptor()
+{
+}
+
+/****************************************
+STATIC: AcceptorDescriptor::StopAcceptor
+****************************************/
+
+void AcceptorDescriptor::StopAcceptor (const unsigned long binding)
+{
+       // TODO: This is something of a hack, or at least it's a static method of the wrong class.
+       AcceptorDescriptor *ad = dynamic_cast <AcceptorDescriptor*> (Bindable_t::GetObject (binding));
+       if (ad)
+               ad->ScheduleClose (false);
+       else
+               throw std::runtime_error ("failed to close nonexistent acceptor");
+}
+
+
+/************************
+AcceptorDescriptor::Read
+************************/
+
+void AcceptorDescriptor::Read()
+{
+       /* Accept up to a certain number of sockets on the listening connection.
+        * Don't try to accept all that are present, because this would allow a DoS attack
+        * in which no data were ever read or written. We should accept more than one,
+        * if available, to keep the partially accepted sockets from backing up in the kernel.
+        */
+
+       /* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it
+        * for readability. According to Stevens UNP, it's possible for an acceptor to select readable
+        * and then block when we call accept. For example, the other end resets the connection after
+        * the socket selects readable and before we call accept. The kernel will remove the dead
+        * socket from the accept queue. If the accept queue is now empty, accept will block.
+        */
+
+
+       struct sockaddr_in pin;
+       socklen_t addrlen = sizeof (pin);
+
+       for (int i=0; i < 10; i++) {
+               int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+               if (sd == INVALID_SOCKET) {
+                       // This breaks the loop when we've accepted everything on the kernel queue,
+                       // up to 10 new connections. But what if the *first* accept fails?
+                       // Does that mean anything serious is happening, beyond the situation
+                       // described in the note above?
+                       break;
+               }
+
+               // Set the newly-accepted socket non-blocking.
+               // On Windows, this may fail because, weirdly, Windows inherits the non-blocking
+               // attribute that we applied to the acceptor socket into the accepted one.
+               if (!SetSocketNonblocking (sd)) {
+               //int val = fcntl (sd, F_GETFL, 0);
+               //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
+                       shutdown (sd, 1);
+                       closesocket (sd);
+                       continue;
+               }
+
+
+               // Disable slow-start (Nagle algorithm). Eventually make this configurable.
+               int one = 1;
+               setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
+
+
+               ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine);
+               if (!cd)
+                       throw std::runtime_error ("no newly accepted connection");
+               cd->SetServerMode();
+               if (EventCallback) {
+                       (*EventCallback) (GetBinding(), EM_CONNECTION_ACCEPTED, NULL, cd->GetBinding());
+               }
+               #ifdef HAVE_EPOLL
+               cd->GetEpollEvent()->events = EPOLLIN | (cd->SelectForWrite() ? EPOLLOUT : 0);
+               #endif
+               assert (MyEventMachine);
+               MyEventMachine->Add (cd);
+               #ifdef HAVE_KQUEUE
+               if (cd->SelectForWrite())
+                       MyEventMachine->ArmKqueueWriter (cd);
+               MyEventMachine->ArmKqueueReader (cd);
+               #endif
+       }
+
+}
+
+
+/*************************
+AcceptorDescriptor::Write
+*************************/
+
+void AcceptorDescriptor::Write()
+{
+  // Why are we here?
+  throw std::runtime_error ("bad code path in acceptor");
+}
+
+
+/*****************************
+AcceptorDescriptor::Heartbeat
+*****************************/
+
+void AcceptorDescriptor::Heartbeat()
+{
+  // No-op
+}
+
+
+/*******************************
+AcceptorDescriptor::GetSockname
+*******************************/
+
+bool AcceptorDescriptor::GetSockname (struct sockaddr *s)
+{
+       bool ok = false;
+       if (s) {
+               socklen_t len = sizeof(*s);
+               int gp = getsockname (GetSocket(), s, &len);
+               if (gp == 0)
+                       ok = true;
+       }
+       return ok;
+}
+
+
+
+/**************************************
+DatagramDescriptor::DatagramDescriptor
+**************************************/
+
+DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
+       EventableDescriptor (sd, parent_em),
+       OutboundDataSize (0),
+       LastIo (gCurrentLoopTime),
+       InactivityTimeout (0)
+{
+       memset (&ReturnAddress, 0, sizeof(ReturnAddress));
+
+       /* Provisionally added 19Oct07. All datagram sockets support broadcasting.
+        * Until now, sending to a broadcast address would give EACCES (permission denied)
+        * on systems like Linux and BSD that require the SO_BROADCAST socket-option in order
+        * to accept a packet to a broadcast address. Solaris doesn't require it. I think
+        * Windows DOES require it but I'm not sure.
+        *
+        * Ruby does NOT do what we're doing here. In Ruby, you have to explicitly set SO_BROADCAST
+        * on a UDP socket in order to enable broadcasting. The reason for requiring the option
+        * in the first place is so that applications don't send broadcast datagrams by mistake.
+        * I imagine that could happen if a user of an application typed in an address that happened
+        * to be a broadcast address on that particular subnet.
+        *
+        * This is provisional because someone may eventually come up with a good reason not to
+        * do it for all UDP sockets. If that happens, then we'll need to add a usercode-level API
+        * to set the socket option, just like Ruby does. AND WE'LL ALSO BREAK CODE THAT DOESN'T
+        * EXPLICITLY SET THE OPTION.
+        */
+
+       int oval = 1;
+       setsockopt (GetSocket(), SOL_SOCKET, SO_BROADCAST, (char*)&oval, sizeof(oval));
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueReader (this);
+       #endif
+}
+
+
+/***************************************
+DatagramDescriptor::~DatagramDescriptor
+***************************************/
+
+DatagramDescriptor::~DatagramDescriptor()
+{
+       // Run down any stranded outbound data.
+       for (size_t i=0; i < OutboundPages.size(); i++)
+               OutboundPages[i].Free();
+}
+
+
+/*****************************
+DatagramDescriptor::Heartbeat
+*****************************/
+
+void DatagramDescriptor::Heartbeat()
+{
+       // Close it if its inactivity timer has expired.
+
+       if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
+               ScheduleClose (false);
+               //bCloseNow = true;
+}
+
+
+/************************
+DatagramDescriptor::Read
+************************/
+
+void DatagramDescriptor::Read()
+{
+       int sd = GetSocket();
+       assert (sd != INVALID_SOCKET);
+       LastIo = gCurrentLoopTime;
+
+       // This is an extremely large read buffer.
+       // In many cases you wouldn't expect to get any more than 4K.
+       char readbuffer [16 * 1024];
+
+       for (int i=0; i < 10; i++) {
+               // Don't read just one buffer and then move on. This is faster
+               // if there is a lot of incoming.
+               // But don't read indefinitely. Give other sockets a chance to run.
+               // NOTICE, we're reading one less than the buffer size.
+               // That's so we can put a guard byte at the end of what we send
+               // to user code.
+
+               struct sockaddr_in sin;
+               socklen_t slen = sizeof (sin);
+               memset (&sin, 0, slen);
+
+               int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen);
+               //cerr << "<R:" << r << ">";
+
+               // In UDP, a zero-length packet is perfectly legal.
+               if (r >= 0) {
+
+                       // Add a null-terminator at the the end of the buffer
+                       // that we will send to the callback.
+                       // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+                       // to be able to depend on this behavior, so they will have
+                       // the option to do some things faster. Additionally it's
+                       // a security guard against buffer overflows.
+                       readbuffer [r] = 0;
+
+
+                       // Set up a "temporary" return address so that callers can "reply" to us
+                       // from within the callback we are about to invoke. That means that ordinary
+                       // calls to "send_data_to_connection" (which is of course misnamed in this
+                       // case) will result in packets being sent back to the same place that sent
+                       // us this one.
+                       // There is a different call (evma_send_datagram) for cases where the caller
+                       // actually wants to send a packet somewhere else.
+
+                       memset (&ReturnAddress, 0, sizeof(ReturnAddress));
+                       memcpy (&ReturnAddress, &sin, slen);
+
+                       _GenericInboundDispatch(readbuffer, r);
+
+               }
+               else {
+                       // Basically a would-block, meaning we've read everything there is to read.
+                       break;
+               }
+
+       }
+
+
+}
+
+
+/*************************
+DatagramDescriptor::Write
+*************************/
+
+void DatagramDescriptor::Write()
+{
+       /* It's possible for a socket to select writable and then no longer
+        * be writable by the time we get around to writing. The kernel might
+        * have used up its available output buffers between the select call
+        * and when we get here. So this condition is not an error.
+        * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData,
+        * but differs in the that the outbound data pages (received from the
+        * user) are _message-structured._ That is, we send each of them out
+        * one message at a time.
+        * TODO, we are currently suppressing the EMSGSIZE error!!!
+        */
+
+       int sd = GetSocket();
+       assert (sd != INVALID_SOCKET);
+       LastIo = gCurrentLoopTime;
+
+       assert (OutboundPages.size() > 0);
+
+       // Send out up to 10 packets, then cycle the machine.
+       for (int i = 0; i < 10; i++) {
+               if (OutboundPages.size() <= 0)
+                       break;
+               OutboundPage *op = &(OutboundPages[0]);
+
+               // The nasty cast to (char*) is needed because Windows is brain-dead.
+               int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From));
+               int e = errno;
+
+               OutboundDataSize -= op->Length;
+               op->Free();
+               OutboundPages.pop_front();
+
+               if (s == SOCKET_ERROR) {
+                       #ifdef OS_UNIX
+                       if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
+                       #endif
+                       #ifdef OS_WIN32
+                       if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
+                       #endif
+                               Close();
+                               break;
+                       }
+               }
+       }
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
+       assert (MyEventMachine);
+       MyEventMachine->Modify (this);
+       #endif
+       #ifdef HAVE_KQUEUE
+       if (SelectForWrite())
+               MyEventMachine->ArmKqueueWriter (this);
+       #endif
+}
+
+
+/**********************************
+DatagramDescriptor::SelectForWrite
+**********************************/
+
+bool DatagramDescriptor::SelectForWrite()
+{
+       /* Changed 15Nov07, per bug report by Mark Zvillius.
+        * The outbound data size will be zero if there are zero-length outbound packets,
+        * so we now select writable in case the outbound page buffer is not empty.
+        * Note that the superclass ShouldDelete method still checks for outbound data size,
+        * which may be wrong.
+        */
+       //return (GetOutboundDataSize() > 0); (Original)
+       return (OutboundPages.size() > 0);
+}
+
+
+/************************************
+DatagramDescriptor::SendOutboundData
+************************************/
+
+int DatagramDescriptor::SendOutboundData (const char *data, int length)
+{
+       // This is an exact clone of ConnectionDescriptor::SendOutboundData.
+       // That means it needs to move to a common ancestor.
+
+       if (IsCloseScheduled())
+       //if (bCloseNow || bCloseAfterWriting)
+               return 0;
+
+       if (!data && (length > 0))
+               throw std::runtime_error ("bad outbound data");
+       char *buffer = (char *) malloc (length + 1);
+       if (!buffer)
+               throw std::runtime_error ("no allocation for outbound data");
+       memcpy (buffer, data, length);
+       buffer [length] = 0;
+       OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress));
+       OutboundDataSize += length;
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = (EPOLLIN | EPOLLOUT);
+       assert (MyEventMachine);
+       MyEventMachine->Modify (this);
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueWriter (this);
+       #endif
+
+       return length;
+}
+
+
+/****************************************
+DatagramDescriptor::SendOutboundDatagram
+****************************************/
+
+int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port)
+{
+       // This is an exact clone of ConnectionDescriptor::SendOutboundData.
+       // That means it needs to move to a common ancestor.
+       // TODO: Refactor this so there's no overlap with SendOutboundData.
+
+       if (IsCloseScheduled())
+       //if (bCloseNow || bCloseAfterWriting)
+               return 0;
+
+       if (!address || !*address || !port)
+               return 0;
+
+       sockaddr_in pin;
+       unsigned long HostAddr;
+
+       HostAddr = inet_addr (address);
+       if (HostAddr == INADDR_NONE) {
+               // The nasty cast to (char*) is because Windows is brain-dead.
+               hostent *hp = gethostbyname ((char*)address);
+               if (!hp)
+                       return 0;
+               HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
+       }
+
+       memset (&pin, 0, sizeof(pin));
+       pin.sin_family = AF_INET;
+       pin.sin_addr.s_addr = HostAddr;
+       pin.sin_port = htons (port);
+
+
+       if (!data && (length > 0))
+               throw std::runtime_error ("bad outbound data");
+       char *buffer = (char *) malloc (length + 1);
+       if (!buffer)
+               throw std::runtime_error ("no allocation for outbound data");
+       memcpy (buffer, data, length);
+       buffer [length] = 0;
+       OutboundPages.push_back (OutboundPage (buffer, length, pin));
+       OutboundDataSize += length;
+
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = (EPOLLIN | EPOLLOUT);
+       assert (MyEventMachine);
+       MyEventMachine->Modify (this);
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueWriter (this);
+       #endif
+
+       return length;
+}
+
+
+/****************************************
+STATIC: DatagramDescriptor::SendDatagram
+****************************************/
+
+int DatagramDescriptor::SendDatagram (const unsigned long binding, const char *data, int length, const char *address, int port)
+{
+       DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
+       if (dd)
+               return dd->SendOutboundDatagram (data, length, address, port);
+       else
+               return -1;
+}
+
+
+/*********************************
+ConnectionDescriptor::GetPeername
+*********************************/
+
+bool ConnectionDescriptor::GetPeername (struct sockaddr *s)
+{
+       bool ok = false;
+       if (s) {
+               socklen_t len = sizeof(*s);
+               int gp = getpeername (GetSocket(), s, &len);
+               if (gp == 0)
+                       ok = true;
+       }
+       return ok;
+}
+
+/*********************************
+ConnectionDescriptor::GetSockname
+*********************************/
+
+bool ConnectionDescriptor::GetSockname (struct sockaddr *s)
+{
+       bool ok = false;
+       if (s) {
+               socklen_t len = sizeof(*s);
+               int gp = getsockname (GetSocket(), s, &len);
+               if (gp == 0)
+                       ok = true;
+       }
+       return ok;
+}
+
+
+/**********************************************
+ConnectionDescriptor::GetCommInactivityTimeout
+**********************************************/
+
+float ConnectionDescriptor::GetCommInactivityTimeout()
+{
+       return ((float)InactivityTimeout / 1000000);
+}
+
+
+/**********************************************
+ConnectionDescriptor::SetCommInactivityTimeout
+**********************************************/
+
+int ConnectionDescriptor::SetCommInactivityTimeout (float value)
+{
+       if (value > 0) {
+               InactivityTimeout = (Int64)(value * 1000000);
+               return 1;
+       }
+       return 0;
+}
+
+/*******************************
+DatagramDescriptor::GetPeername
+*******************************/
+
+bool DatagramDescriptor::GetPeername (struct sockaddr *s)
+{
+       bool ok = false;
+       if (s) {
+               memset (s, 0, sizeof(struct sockaddr));
+               memcpy (s, &ReturnAddress, sizeof(ReturnAddress));
+               ok = true;
+       }
+       return ok;
+}
+
+/*******************************
+DatagramDescriptor::GetSockname
+*******************************/
+
+bool DatagramDescriptor::GetSockname (struct sockaddr *s)
+{
+       bool ok = false;
+       if (s) {
+               socklen_t len = sizeof(*s);
+               int gp = getsockname (GetSocket(), s, &len);
+               if (gp == 0)
+                       ok = true;
+       }
+       return ok;
+}
+
+
+
+/********************************************
+DatagramDescriptor::GetCommInactivityTimeout
+********************************************/
+
+float DatagramDescriptor::GetCommInactivityTimeout()
+{
+       return ((float)InactivityTimeout / 1000000);
+}
+
+/********************************************
+DatagramDescriptor::SetCommInactivityTimeout
+********************************************/
+
+int DatagramDescriptor::SetCommInactivityTimeout (float value)
+{
+       if (value > 0) {
+               InactivityTimeout = (Int64)(value * 1000000);
+               return 1;
+       }
+       return 0;
+}
+
+
+/************************************
+InotifyDescriptor::InotifyDescriptor
+*************************************/
+
+InotifyDescriptor::InotifyDescriptor (EventMachine_t *em):
+       EventableDescriptor(0, em)
+{
+       bCallbackUnbind = false;
+
+       #ifndef HAVE_INOTIFY
+       throw std::runtime_error("no inotify support on this system");
+       #else
+
+       int fd = inotify_init();
+       if (fd == -1) {
+               char buf[200];
+               snprintf (buf, sizeof(buf)-1, "unable to create inotify descriptor: %s", strerror(errno));
+               throw std::runtime_error (buf);
+       }
+
+       MySocket = fd;
+       SetSocketNonblocking(MySocket);
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+
+       #endif
+}
+
+
+/*************************************
+InotifyDescriptor::~InotifyDescriptor
+**************************************/
+
+InotifyDescriptor::~InotifyDescriptor()
+{
+       close(MySocket);
+       MySocket = INVALID_SOCKET;
+}
+
+/***********************
+InotifyDescriptor::Read
+************************/
+
+void InotifyDescriptor::Read()
+{
+       assert (MyEventMachine);
+       MyEventMachine->_ReadInotifyEvents();
+}
+
+
+/************************
+InotifyDescriptor::Write
+*************************/
+
+void InotifyDescriptor::Write()
+{
+       throw std::runtime_error("bad code path in inotify");
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ed.h
new file mode 100644 (file)
index 0000000..d60afff
--- /dev/null
@@ -0,0 +1,424 @@
+/*****************************************************************************
+
+$Id$
+
+File:     ed.h
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#ifndef __EventableDescriptor__H_
+#define __EventableDescriptor__H_
+
+
+class EventMachine_t; // forward reference
+#ifdef WITH_SSL
+class SslBox_t; // forward reference
+#endif
+
+bool SetSocketNonblocking (SOCKET);
+
+
+/*************************
+class EventableDescriptor
+*************************/
+
+class EventableDescriptor: public Bindable_t
+{
+       public:
+               EventableDescriptor (int, EventMachine_t*);
+               virtual ~EventableDescriptor();
+
+               int GetSocket() {return MySocket;}
+               void SetSocketInvalid() { MySocket = INVALID_SOCKET; }
+               void Close();
+
+               virtual void Read() = 0;
+               virtual void Write() = 0;
+               virtual void Heartbeat() = 0;
+
+               // These methods tell us whether the descriptor
+               // should be selected or polled for read/write.
+               virtual bool SelectForRead() = 0;
+               virtual bool SelectForWrite() = 0;
+
+               // are we scheduled for a close, or in an error state, or already closed?
+               bool ShouldDelete();
+               // Do we have any data to write? This is used by ShouldDelete.
+               virtual int GetOutboundDataSize() {return 0;}
+               virtual bool IsWatchOnly(){ return false; }
+
+               virtual void ScheduleClose (bool after_writing);
+               bool IsCloseScheduled();
+               virtual void HandleError(){ ScheduleClose (false); }
+
+               void SetEventCallback (void (*cb)(const unsigned long, int, const char*, const unsigned long));
+
+               virtual bool GetPeername (struct sockaddr*) {return false;}
+               virtual bool GetSockname (struct sockaddr*) {return false;}
+               virtual bool GetSubprocessPid (pid_t*) {return false;}
+
+               virtual void StartTls() {}
+               virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) {}
+
+               #ifdef WITH_SSL
+               virtual X509 *GetPeerCert() {return NULL;}
+               #endif
+
+               virtual float GetCommInactivityTimeout() {return 0.0;}
+               virtual int SetCommInactivityTimeout (float value) {return 0;}
+               float GetPendingConnectTimeout();
+               int SetPendingConnectTimeout (float value);
+
+               #ifdef HAVE_EPOLL
+               struct epoll_event *GetEpollEvent() { return &EpollEvent; }
+               #endif
+
+               virtual void StartProxy(const unsigned long, const unsigned long);
+               virtual void StopProxy();
+               virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
+               virtual int SendOutboundData(const char*,int){ return -1; }
+               virtual bool IsPaused(){ return false; }
+               virtual bool Pause(){ return false; }
+               virtual bool Resume(){ return false; }
+
+       private:
+               bool bCloseNow;
+               bool bCloseAfterWriting;
+
+       protected:
+               int MySocket;
+
+               void (*EventCallback)(const unsigned long, int, const char*, const unsigned long);
+               void _GenericInboundDispatch(const char*, int);
+
+               Int64 CreatedAt;
+               bool bCallbackUnbind;
+               int UnbindReasonCode;
+               EventableDescriptor *ProxyTarget;
+               EventableDescriptor *ProxiedFrom;
+
+               unsigned long MaxOutboundBufSize;
+
+               #ifdef HAVE_EPOLL
+               struct epoll_event EpollEvent;
+               #endif
+
+               EventMachine_t *MyEventMachine;
+               int PendingConnectTimeout;
+};
+
+
+
+/*************************
+class LoopbreakDescriptor
+*************************/
+
+class LoopbreakDescriptor: public EventableDescriptor
+{
+       public:
+               LoopbreakDescriptor (int, EventMachine_t*);
+               virtual ~LoopbreakDescriptor() {}
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat() {}
+
+               virtual bool SelectForRead() {return true;}
+               virtual bool SelectForWrite() {return false;}
+};
+
+
+/**************************
+class ConnectionDescriptor
+**************************/
+
+class ConnectionDescriptor: public EventableDescriptor
+{
+       public:
+               ConnectionDescriptor (int, EventMachine_t*);
+               virtual ~ConnectionDescriptor();
+
+               static int SendDataToConnection (const unsigned long, const char*, int);
+               static void CloseConnection (const unsigned long, bool);
+               static int ReportErrorStatus (const unsigned long);
+
+               int SendOutboundData (const char*, int);
+
+               void SetConnectPending (bool f);
+               virtual void ScheduleClose (bool after_writing);
+               virtual void HandleError();
+
+               void SetNotifyReadable (bool);
+               void SetNotifyWritable (bool);
+               void SetWatchOnly (bool);
+
+               bool IsPaused(){ return bPaused; }
+               bool Pause();
+               bool Resume();
+
+               bool IsNotifyReadable(){ return bNotifyReadable; }
+               bool IsNotifyWritable(){ return bNotifyWritable; }
+               virtual bool IsWatchOnly(){ return bWatchOnly; }
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat();
+
+               virtual bool SelectForRead();
+               virtual bool SelectForWrite();
+
+               // Do we have any data to write? This is used by ShouldDelete.
+               virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+               virtual void StartTls();
+               virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer);
+
+               #ifdef WITH_SSL
+               virtual X509 *GetPeerCert();
+               virtual bool VerifySslPeer(const char*);
+               virtual void AcceptSslPeer();
+               #endif
+
+               void SetServerMode() {bIsServer = true;}
+
+               virtual bool GetPeername (struct sockaddr*);
+               virtual bool GetSockname (struct sockaddr*);
+
+               virtual float GetCommInactivityTimeout();
+               virtual int SetCommInactivityTimeout (float value);
+
+
+       protected:
+               struct OutboundPage {
+                       OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
+                       void Free() {if (Buffer) free ((char*)Buffer); }
+                       const char *Buffer;
+                       int Length;
+                       int Offset;
+               };
+
+       protected:
+               bool bPaused;
+               bool bConnectPending;
+
+               bool bNotifyReadable;
+               bool bNotifyWritable;
+               bool bWatchOnly;
+
+               bool bReadAttemptedAfterClose;
+               bool bWriteAttemptedAfterClose;
+
+               deque<OutboundPage> OutboundPages;
+               int OutboundDataSize;
+
+               #ifdef WITH_SSL
+               SslBox_t *SslBox;
+               std::string CertChainFilename;
+               std::string PrivateKeyFilename;
+               bool bHandshakeSignaled;
+               bool bSslVerifyPeer;
+               bool bSslPeerAccepted;
+               #endif
+
+               #ifdef HAVE_KQUEUE
+               bool bGotExtraKqueueEvent;
+               #endif
+
+               bool bIsServer;
+               Int64 LastIo;
+               int InactivityTimeout;
+
+       private:
+               void _UpdateEvents();
+               void _UpdateEvents(bool, bool);
+               void _WriteOutboundData();
+               void _DispatchInboundData (const char *buffer, int size);
+               void _DispatchCiphertext();
+               int _SendRawOutboundData (const char*, int);
+               int _ReportErrorStatus();
+               void _CheckHandshakeStatus();
+
+};
+
+
+/************************
+class DatagramDescriptor
+************************/
+
+class DatagramDescriptor: public EventableDescriptor
+{
+       public:
+               DatagramDescriptor (int, EventMachine_t*);
+               virtual ~DatagramDescriptor();
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat();
+
+               virtual bool SelectForRead() {return true;}
+               virtual bool SelectForWrite();
+
+               int SendOutboundData (const char*, int);
+               int SendOutboundDatagram (const char*, int, const char*, int);
+
+               // Do we have any data to write? This is used by ShouldDelete.
+               virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+               virtual bool GetPeername (struct sockaddr*);
+               virtual bool GetSockname (struct sockaddr*);
+
+    virtual float GetCommInactivityTimeout();
+    virtual int SetCommInactivityTimeout (float value);
+
+               static int SendDatagram (const unsigned long, const char*, int, const char*, int);
+
+
+       protected:
+               struct OutboundPage {
+                       OutboundPage (const char *b, int l, struct sockaddr_in f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
+                       void Free() {if (Buffer) free ((char*)Buffer); }
+                       const char *Buffer;
+                       int Length;
+                       int Offset;
+                       struct sockaddr_in From;
+               };
+
+               deque<OutboundPage> OutboundPages;
+               int OutboundDataSize;
+
+               struct sockaddr_in ReturnAddress;
+
+               Int64 LastIo;
+               int InactivityTimeout;
+};
+
+
+/************************
+class AcceptorDescriptor
+************************/
+
+class AcceptorDescriptor: public EventableDescriptor
+{
+       public:
+               AcceptorDescriptor (int, EventMachine_t*);
+               virtual ~AcceptorDescriptor();
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat();
+
+               virtual bool SelectForRead() {return true;}
+               virtual bool SelectForWrite() {return false;}
+
+               virtual bool GetSockname (struct sockaddr*);
+
+               static void StopAcceptor (const unsigned long binding);
+};
+
+/********************
+class PipeDescriptor
+********************/
+
+#ifdef OS_UNIX
+class PipeDescriptor: public EventableDescriptor
+{
+       public:
+               PipeDescriptor (int, pid_t, EventMachine_t*);
+               virtual ~PipeDescriptor();
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat();
+
+               virtual bool SelectForRead();
+               virtual bool SelectForWrite();
+
+               int SendOutboundData (const char*, int);
+               virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+               virtual bool GetSubprocessPid (pid_t*);
+
+       protected:
+               struct OutboundPage {
+                       OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
+                       void Free() {if (Buffer) free ((char*)Buffer); }
+                       const char *Buffer;
+                       int Length;
+                       int Offset;
+               };
+
+       protected:
+               bool bReadAttemptedAfterClose;
+               Int64 LastIo;
+               int InactivityTimeout;
+
+               deque<OutboundPage> OutboundPages;
+               int OutboundDataSize;
+
+               pid_t SubprocessPid;
+
+       private:
+               void _DispatchInboundData (const char *buffer, int size);
+};
+#endif // OS_UNIX
+
+
+/************************
+class KeyboardDescriptor
+************************/
+
+class KeyboardDescriptor: public EventableDescriptor
+{
+       public:
+               KeyboardDescriptor (EventMachine_t*);
+               virtual ~KeyboardDescriptor();
+
+               virtual void Read();
+               virtual void Write();
+               virtual void Heartbeat();
+
+               virtual bool SelectForRead() {return true;}
+               virtual bool SelectForWrite() {return false;}
+
+       protected:
+               bool bReadAttemptedAfterClose;
+               Int64 LastIo;
+               int InactivityTimeout;
+
+       private:
+               void _DispatchInboundData (const char *buffer, int size);
+};
+
+
+/***********************
+class InotifyDescriptor
+************************/
+
+class InotifyDescriptor: public EventableDescriptor
+{
+       public:
+               InotifyDescriptor (EventMachine_t*);
+               virtual ~InotifyDescriptor();
+
+               void Read();
+               void Write();
+
+               virtual void Heartbeat() {}
+               virtual bool SelectForRead() {return true;}
+               virtual bool SelectForWrite() {return false;}
+};
+
+#endif // __EventableDescriptor__H_
+
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.cpp
new file mode 100644 (file)
index 0000000..a53699b
--- /dev/null
@@ -0,0 +1,2282 @@
+/*****************************************************************************
+
+$Id$
+
+File:     em.cpp
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+// THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.
+//#ifdef OS_UNIX
+
+
+#include "project.h"
+
+// Keep a global variable floating around
+// with the current loop time as set by the Event Machine.
+// This avoids the need for frequent expensive calls to time(NULL);
+Int64 gCurrentLoopTime;
+
+#ifdef OS_WIN32
+unsigned gTickCountTickover;
+unsigned gLastTickCount;
+#endif
+
+
+/* The numer of max outstanding timers was once a const enum defined in em.h.
+ * Now we define it here so that users can change its value if necessary.
+ */
+static unsigned int MaxOutstandingTimers = 10000;
+
+
+/* Internal helper to convert strings to internet addresses. IPv6-aware.
+ * Not reentrant or threadsafe, optimized for speed.
+ */
+static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size);
+
+/***************************************
+STATIC EventMachine_t::GetMaxTimerCount
+***************************************/
+
+int EventMachine_t::GetMaxTimerCount()
+{
+       return MaxOutstandingTimers;
+}
+
+
+/***************************************
+STATIC EventMachine_t::SetMaxTimerCount
+***************************************/
+
+void EventMachine_t::SetMaxTimerCount (int count)
+{
+       /* Allow a user to increase the maximum number of outstanding timers.
+        * If this gets "too high" (a metric that is of course platform dependent),
+        * bad things will happen like performance problems and possible overuse
+        * of memory.
+        * The actual timer mechanism is very efficient so it's hard to know what
+        * the practical max, but 100,000 shouldn't be too problematical.
+        */
+       if (count < 100)
+               count = 100;
+       MaxOutstandingTimers = count;
+}
+
+
+
+/******************************
+EventMachine_t::EventMachine_t
+******************************/
+
+EventMachine_t::EventMachine_t (void (*event_callback)(const unsigned long, int, const char*, const unsigned long)):
+       HeartbeatInterval(2000000),
+       EventCallback (event_callback),
+       NextHeartbeatTime (0),
+       LoopBreakerReader (-1),
+       LoopBreakerWriter (-1),
+       bEpoll (false),
+       epfd (-1),
+       bKqueue (false),
+       kqfd (-1),
+       inotify (NULL)
+{
+       // Default time-slice is just smaller than one hundred mills.
+       Quantum.tv_sec = 0;
+       Quantum.tv_usec = 90000;
+
+       gTerminateSignalReceived = false;
+       // Make sure the current loop time is sane, in case we do any initializations of
+       // objects before we start running.
+       _UpdateTime();
+
+       /* We initialize the network library here (only on Windows of course)
+        * and initialize "loop breakers." Our destructor also does some network-level
+        * cleanup. There's thus an implicit assumption that any given instance of EventMachine_t
+        * will only call ::Run once. Is that a good assumption? Should we move some of these
+        * inits and de-inits into ::Run?
+        */
+       #ifdef OS_WIN32
+       WSADATA w;
+       WSAStartup (MAKEWORD (1, 1), &w);
+       #endif
+
+       _InitializeLoopBreaker();
+}
+
+
+/*******************************
+EventMachine_t::~EventMachine_t
+*******************************/
+
+EventMachine_t::~EventMachine_t()
+{
+       // Run down descriptors
+       size_t i;
+       for (i = 0; i < NewDescriptors.size(); i++)
+               delete NewDescriptors[i];
+       for (i = 0; i < Descriptors.size(); i++)
+               delete Descriptors[i];
+
+       close (LoopBreakerReader);
+       close (LoopBreakerWriter);
+
+       // Remove any file watch descriptors
+       while(!Files.empty()) {
+               map<int, Bindable_t*>::iterator f = Files.begin();
+               UnwatchFile (f->first);
+       }
+
+       if (epfd != -1)
+               close (epfd);
+       if (kqfd != -1)
+               close (kqfd);
+}
+
+
+/*************************
+EventMachine_t::_UseEpoll
+*************************/
+
+void EventMachine_t::_UseEpoll()
+{
+       /* Temporary.
+        * Use an internal flag to switch in epoll-based functionality until we determine
+        * how it should be integrated properly and the extent of the required changes.
+        * A permanent solution needs to allow the integration of additional technologies,
+        * like kqueue and Solaris's events.
+        */
+
+       #ifdef HAVE_EPOLL
+       bEpoll = true;
+       #endif
+}
+
+/**************************
+EventMachine_t::_UseKqueue
+**************************/
+
+void EventMachine_t::_UseKqueue()
+{
+       /* Temporary.
+        * See comments under _UseEpoll.
+        */
+
+       #ifdef HAVE_KQUEUE
+       bKqueue = true;
+       #endif
+}
+
+
+/****************************
+EventMachine_t::ScheduleHalt
+****************************/
+
+void EventMachine_t::ScheduleHalt()
+{
+  /* This is how we stop the machine.
+   * This can be called by clients. Signal handlers will probably
+   * set the global flag.
+   * For now this means there can only be one EventMachine ever running at a time.
+   *
+   * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here,
+   * because it may be called from signal handlers invoked from code that we don't
+   * control. At this writing (20Sep06), EM does NOT install any signal handlers of
+   * its own.
+   *
+   * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
+   * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
+   */
+       gTerminateSignalReceived = true;
+}
+
+
+
+/*******************************
+EventMachine_t::SetTimerQuantum
+*******************************/
+
+void EventMachine_t::SetTimerQuantum (int interval)
+{
+       /* We get a timer-quantum expressed in milliseconds.
+        * Don't set a quantum smaller than 5 or larger than 2500.
+        */
+
+       if ((interval < 5) || (interval > 2500))
+               throw std::runtime_error ("invalid timer-quantum");
+
+       Quantum.tv_sec = interval / 1000;
+       Quantum.tv_usec = (interval % 1000) * 1000;
+}
+
+
+/*************************************
+(STATIC) EventMachine_t::SetuidString
+*************************************/
+
+void EventMachine_t::SetuidString (const char *username)
+{
+    /* This method takes a caller-supplied username and tries to setuid
+     * to that user. There is no meaningful implementation (and no error)
+     * on Windows. On Unix, a failure to setuid the caller-supplied string
+     * causes a fatal abort, because presumably the program is calling here
+     * in order to fulfill a security requirement. If we fail silently,
+     * the user may continue to run with too much privilege.
+     *
+     * TODO, we need to decide on and document a way of generating C++ level errors
+     * that can be wrapped in documented Ruby exceptions, so users can catch
+     * and handle them. And distinguish it from errors that we WON'T let the Ruby
+     * user catch (like security-violations and resource-overallocation).
+     * A setuid failure here would be in the latter category.
+     */
+
+    #ifdef OS_UNIX
+    if (!username || !*username)
+       throw std::runtime_error ("setuid_string failed: no username specified");
+
+    struct passwd *p = getpwnam (username);
+    if (!p)
+       throw std::runtime_error ("setuid_string failed: unknown username");
+
+    if (setuid (p->pw_uid) != 0)
+       throw std::runtime_error ("setuid_string failed: no setuid");
+
+    // Success.
+    #endif
+}
+
+
+/****************************************
+(STATIC) EventMachine_t::SetRlimitNofile
+****************************************/
+
+int EventMachine_t::SetRlimitNofile (int nofiles)
+{
+       #ifdef OS_UNIX
+       struct rlimit rlim;
+       getrlimit (RLIMIT_NOFILE, &rlim);
+       if (nofiles >= 0) {
+               rlim.rlim_cur = nofiles;
+               if ((unsigned int)nofiles > rlim.rlim_max)
+                       rlim.rlim_max = nofiles;
+               setrlimit (RLIMIT_NOFILE, &rlim);
+               // ignore the error return, for now at least.
+               // TODO, emit an error message someday when we have proper debug levels.
+       }
+       getrlimit (RLIMIT_NOFILE, &rlim);
+       return rlim.rlim_cur;
+       #endif
+
+       #ifdef OS_WIN32
+       // No meaningful implementation on Windows.
+       return 0;
+       #endif
+}
+
+
+/*********************************
+EventMachine_t::SignalLoopBreaker
+*********************************/
+
+void EventMachine_t::SignalLoopBreaker()
+{
+       #ifdef OS_UNIX
+       write (LoopBreakerWriter, "", 1);
+       #endif
+       #ifdef OS_WIN32
+       sendto (LoopBreakerReader, "", 0, 0, (struct sockaddr*)&(LoopBreakerTarget), sizeof(LoopBreakerTarget));
+       #endif
+}
+
+
+/**************************************
+EventMachine_t::_InitializeLoopBreaker
+**************************************/
+
+void EventMachine_t::_InitializeLoopBreaker()
+{
+       /* A "loop-breaker" is a socket-descriptor that we can write to in order
+        * to break the main select loop. Primarily useful for things running on
+        * threads other than the main EM thread, so they can trigger processing
+        * of events that arise exogenously to the EM.
+        * Keep the loop-breaker pipe out of the main descriptor set, otherwise
+        * its events will get passed on to user code.
+        */
+
+       #ifdef OS_UNIX
+       int fd[2];
+       if (pipe (fd))
+               throw std::runtime_error ("no loop breaker");
+
+       LoopBreakerWriter = fd[1];
+       LoopBreakerReader = fd[0];
+       #endif
+
+       #ifdef OS_WIN32
+       int sd = socket (AF_INET, SOCK_DGRAM, 0);
+       if (sd == INVALID_SOCKET)
+               throw std::runtime_error ("no loop breaker socket");
+       SetSocketNonblocking (sd);
+
+       memset (&LoopBreakerTarget, 0, sizeof(LoopBreakerTarget));
+       LoopBreakerTarget.sin_family = AF_INET;
+       LoopBreakerTarget.sin_addr.s_addr = inet_addr ("127.0.0.1");
+
+       srand ((int)time(NULL));
+       int i;
+       for (i=0; i < 100; i++) {
+               int r = (rand() % 10000) + 20000;
+               LoopBreakerTarget.sin_port = htons (r);
+               if (bind (sd, (struct sockaddr*)&LoopBreakerTarget, sizeof(LoopBreakerTarget)) == 0)
+                       break;
+       }
+
+       if (i == 100)
+               throw std::runtime_error ("no loop breaker");
+       LoopBreakerReader = sd;
+       #endif
+}
+
+/***************************
+EventMachine_t::_UpdateTime
+***************************/
+
+void EventMachine_t::_UpdateTime()
+{
+       #if defined(OS_UNIX)
+       struct timeval tv;
+       gettimeofday (&tv, NULL);
+       gCurrentLoopTime = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
+
+       #elif defined(OS_WIN32)
+       unsigned tick = GetTickCount();
+       if (tick < gLastTickCount)
+               gTickCountTickover += 1;
+       gLastTickCount = tick;
+       gCurrentLoopTime = ((Int64)gTickCountTickover << 32) + (Int64)tick;
+
+       #else
+       gCurrentLoopTime = (Int64)time(NULL) * 1000000LL;
+       #endif
+}
+
+/*******************
+EventMachine_t::Run
+*******************/
+
+void EventMachine_t::Run()
+{
+       #ifdef OS_WIN32
+       HookControlC (true);
+       #endif
+
+       #ifdef HAVE_EPOLL
+       if (bEpoll) {
+               epfd = epoll_create (MaxEpollDescriptors);
+               if (epfd == -1) {
+                       char buf[200];
+                       snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+               int cloexec = fcntl (epfd, F_GETFD, 0);
+               assert (cloexec >= 0);
+               cloexec |= FD_CLOEXEC;
+               fcntl (epfd, F_SETFD, cloexec);
+
+               assert (LoopBreakerReader >= 0);
+               LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+               assert (ld);
+               Add (ld);
+       }
+       #endif
+
+       #ifdef HAVE_KQUEUE
+       if (bKqueue) {
+               kqfd = kqueue();
+               if (kqfd == -1) {
+                       char buf[200];
+                       snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+               // cloexec not needed. By definition, kqueues are not carried across forks.
+
+               assert (LoopBreakerReader >= 0);
+               LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+               assert (ld);
+               Add (ld);
+       }
+       #endif
+
+       while (true) {
+               _UpdateTime();
+               if (!_RunTimers())
+                       break;
+
+               /* _Add must precede _Modify because the same descriptor might
+                * be on both lists during the same pass through the machine,
+                * and to modify a descriptor before adding it would fail.
+                */
+               _AddNewDescriptors();
+               _ModifyDescriptors();
+
+               if (!_RunOnce())
+                       break;
+               if (gTerminateSignalReceived)
+                       break;
+       }
+
+       #ifdef OS_WIN32
+       HookControlC (false);
+       #endif
+}
+
+
+/************************
+EventMachine_t::_RunOnce
+************************/
+
+bool EventMachine_t::_RunOnce()
+{
+       if (bEpoll)
+               return _RunEpollOnce();
+       else if (bKqueue)
+               return _RunKqueueOnce();
+       else
+               return _RunSelectOnce();
+}
+
+
+
+/*****************************
+EventMachine_t::_RunEpollOnce
+*****************************/
+
+bool EventMachine_t::_RunEpollOnce()
+{
+       #ifdef HAVE_EPOLL
+       assert (epfd != -1);
+       int s;
+
+       #ifdef BUILD_FOR_RUBY
+       TRAP_BEG;
+       #endif
+       s = epoll_wait (epfd, epoll_events, MaxEvents, 50);
+       #ifdef BUILD_FOR_RUBY
+       TRAP_END;
+       #endif
+
+       if (s > 0) {
+               for (int i=0; i < s; i++) {
+                       EventableDescriptor *ed = (EventableDescriptor*) epoll_events[i].data.ptr;
+
+                       if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
+                               continue;
+
+                       assert(ed->GetSocket() != INVALID_SOCKET);
+
+                       if (epoll_events[i].events & EPOLLIN)
+                               ed->Read();
+                       if (epoll_events[i].events & EPOLLOUT)
+                               ed->Write();
+                       if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
+                               ed->HandleError();
+               }
+       }
+       else if (s < 0) {
+               // epoll_wait can fail on error in a handful of ways.
+               // If this happens, then wait for a little while to avoid busy-looping.
+               // If the error was EINTR, we probably caught SIGCHLD or something,
+               // so keep the wait short.
+               timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
+               EmSelect (0, NULL, NULL, NULL, &tv);
+       }
+
+       { // cleanup dying sockets
+               // vector::pop_back works in constant time.
+               // TODO, rip this out and only delete the descriptors we know have died,
+               // rather than traversing the whole list.
+               //  Modified 05Jan08 per suggestions by Chris Heath. It's possible that
+               //  an EventableDescriptor will have a descriptor value of -1. That will
+               //  happen if EventableDescriptor::Close was called on it. In that case,
+               //  don't call epoll_ctl to remove the socket's filters from the epoll set.
+               //  According to the epoll docs, this happens automatically when the
+               //  descriptor is closed anyway. This is different from the case where
+               //  the socket has already been closed but the descriptor in the ED object
+               //  hasn't yet been set to INVALID_SOCKET.
+               int i, j;
+               int nSockets = Descriptors.size();
+               for (i=0, j=0; i < nSockets; i++) {
+                       EventableDescriptor *ed = Descriptors[i];
+                       assert (ed);
+                       if (ed->ShouldDelete()) {
+                               if (ed->GetSocket() != INVALID_SOCKET) {
+                                       assert (bEpoll); // wouldn't be in this method otherwise.
+                                       assert (epfd != -1);
+                                       int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
+                                       // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
+                                       if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
+                                               char buf [200];
+                                               snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
+                                               throw std::runtime_error (buf);
+                                       }
+                               }
+
+                               ModifiedDescriptors.erase (ed);
+                               delete ed;
+                       }
+                       else
+                               Descriptors [j++] = ed;
+               }
+               while ((size_t)j < Descriptors.size())
+                       Descriptors.pop_back();
+
+       }
+
+       // TODO, heartbeats.
+       // Added 14Sep07, its absence was noted by Brian Candler. But the comment was here, indicated
+       // that this got thought about and not done when EPOLL was originally written. Was there a reason
+       // not to do it, or was it an oversight? Certainly, running a heartbeat on 50,000 connections every
+       // two seconds can get to be a real bear, especially if all we're doing is timing out dead ones.
+       // Maybe there's a better way to do this. (Or maybe it's not that expensive after all.)
+       //
+       { // dispatch heartbeats
+               if (gCurrentLoopTime >= NextHeartbeatTime) {
+                       NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
+
+                       for (int i=0; i < Descriptors.size(); i++) {
+                               EventableDescriptor *ed = Descriptors[i];
+                               assert (ed);
+                               ed->Heartbeat();
+                       }
+               }
+       }
+
+       #ifdef BUILD_FOR_RUBY
+       if (!rb_thread_alone()) {
+               rb_thread_schedule();
+       }
+       #endif
+
+       return true;
+       #else
+       throw std::runtime_error ("epoll is not implemented on this platform");
+       #endif
+}
+
+
+/******************************
+EventMachine_t::_RunKqueueOnce
+******************************/
+
+bool EventMachine_t::_RunKqueueOnce()
+{
+       #ifdef HAVE_KQUEUE
+       assert (kqfd != -1);
+       struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
+
+       int k;
+       #ifdef BUILD_FOR_RUBY
+       TRAP_BEG;
+       #endif
+       k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
+       #ifdef BUILD_FOR_RUBY
+       TRAP_END;
+       #endif
+
+       struct kevent *ke = Karray;
+       while (k > 0) {
+               switch (ke->filter)
+               {
+                       case EVFILT_VNODE:
+                               _HandleKqueueFileEvent (ke);
+                               break;
+
+                       case EVFILT_PROC:
+                               _HandleKqueuePidEvent (ke);
+                               break;
+
+                       case EVFILT_READ:
+                       case EVFILT_WRITE:
+                               EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
+                               assert (ed);
+
+                               if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
+                                       break;
+
+                               if (ke->filter == EVFILT_READ)
+                                       ed->Read();
+                               else if (ke->filter == EVFILT_WRITE)
+                                       ed->Write();
+                               else
+                                       cerr << "Discarding unknown kqueue event " << ke->filter << endl;
+
+                               break;
+               }
+
+               --k;
+               ++ke;
+       }
+
+       { // cleanup dying sockets
+               // vector::pop_back works in constant time.
+               // TODO, rip this out and only delete the descriptors we know have died,
+               // rather than traversing the whole list.
+               // In kqueue, closing a descriptor automatically removes its event filters.
+
+               int i, j;
+               int nSockets = Descriptors.size();
+               for (i=0, j=0; i < nSockets; i++) {
+                       EventableDescriptor *ed = Descriptors[i];
+                       assert (ed);
+                       if (ed->ShouldDelete()) {
+                               ModifiedDescriptors.erase (ed);
+                               delete ed;
+                       }
+                       else
+                               Descriptors [j++] = ed;
+               }
+               while ((size_t)j < Descriptors.size())
+                       Descriptors.pop_back();
+
+       }
+
+       { // dispatch heartbeats
+               if (gCurrentLoopTime >= NextHeartbeatTime) {
+                       NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
+
+                       for (unsigned int i=0; i < Descriptors.size(); i++) {
+                               EventableDescriptor *ed = Descriptors[i];
+                               assert (ed);
+                               ed->Heartbeat();
+                       }
+               }
+       }
+
+
+       // TODO, replace this with rb_thread_blocking_region for 1.9 builds.
+       #ifdef BUILD_FOR_RUBY
+       if (!rb_thread_alone()) {
+               rb_thread_schedule();
+       }
+       #endif
+
+       return true;
+       #else
+       throw std::runtime_error ("kqueue is not implemented on this platform");
+       #endif
+}
+
+
+/*********************************
+EventMachine_t::_ModifyEpollEvent
+*********************************/
+
+void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
+{
+       #ifdef HAVE_EPOLL
+       if (bEpoll) {
+               assert (epfd != -1);
+               assert (ed);
+               assert (ed->GetSocket() != INVALID_SOCKET);
+               int e = epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
+               if (e) {
+                       char buf [200];
+                       snprintf (buf, sizeof(buf)-1, "unable to modify epoll event: %s", strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+       }
+       #endif
+}
+
+
+
+/**************************
+SelectData_t::SelectData_t
+**************************/
+
+SelectData_t::SelectData_t()
+{
+       maxsocket = 0;
+       FD_ZERO (&fdreads);
+       FD_ZERO (&fdwrites);
+       FD_ZERO (&fderrors);
+}
+
+
+#ifdef BUILD_FOR_RUBY
+/*****************
+_SelectDataSelect
+*****************/
+
+#ifdef HAVE_TBR
+static VALUE _SelectDataSelect (void *v)
+{
+       SelectData_t *sd = (SelectData_t*)v;
+       sd->nSockets = select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), &(sd->fderrors), &(sd->tv));
+       return Qnil;
+}
+#endif
+
+/*********************
+SelectData_t::_Select
+*********************/
+
+int SelectData_t::_Select()
+{
+       #ifdef HAVE_TBR
+       rb_thread_blocking_region (_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0);
+       return nSockets;
+       #endif
+
+       #ifndef HAVE_TBR
+       return EmSelect (maxsocket+1, &fdreads, &fdwrites, &fderrors, &tv);
+       #endif
+}
+#endif
+
+
+
+/******************************
+EventMachine_t::_RunSelectOnce
+******************************/
+
+bool EventMachine_t::_RunSelectOnce()
+{
+       // Crank the event machine once.
+       // If there are no descriptors to process, then sleep
+       // for a few hundred mills to avoid busy-looping.
+       // Return T/F to indicate whether we should continue.
+       // This is based on a select loop. Alternately provide epoll
+       // if we know we're running on a 2.6 kernel.
+       // epoll will be effective if we provide it as an alternative,
+       // however it has the same problem interoperating with Ruby
+       // threads that select does.
+
+       //cerr << "X";
+
+       /* This protection is now obsolete, because we will ALWAYS
+        * have at least one descriptor (the loop-breaker) to read.
+        */
+       /*
+       if (Descriptors.size() == 0) {
+               #ifdef OS_UNIX
+               timeval tv = {0, 200 * 1000};
+               EmSelect (0, NULL, NULL, NULL, &tv);
+               return true;
+               #endif
+               #ifdef OS_WIN32
+               Sleep (200);
+               return true;
+               #endif
+       }
+       */
+
+       SelectData_t SelectData;
+       /*
+       fd_set fdreads, fdwrites;
+       FD_ZERO (&fdreads);
+       FD_ZERO (&fdwrites);
+
+       int maxsocket = 0;
+       */
+
+       // Always read the loop-breaker reader.
+       // Changed 23Aug06, provisionally implemented for Windows with a UDP socket
+       // running on localhost with a randomly-chosen port. (*Puke*)
+       // Windows has a version of the Unix pipe() library function, but it doesn't
+       // give you back descriptors that are selectable.
+       FD_SET (LoopBreakerReader, &(SelectData.fdreads));
+       if (SelectData.maxsocket < LoopBreakerReader)
+               SelectData.maxsocket = LoopBreakerReader;
+
+       // prepare the sockets for reading and writing
+       size_t i;
+       for (i = 0; i < Descriptors.size(); i++) {
+               EventableDescriptor *ed = Descriptors[i];
+               assert (ed);
+               int sd = ed->GetSocket();
+               if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
+                       continue;
+               assert (sd != INVALID_SOCKET);
+
+               if (ed->SelectForRead())
+                       FD_SET (sd, &(SelectData.fdreads));
+               if (ed->SelectForWrite())
+                       FD_SET (sd, &(SelectData.fdwrites));
+
+               #ifdef OS_WIN32
+               /* 21Sep09: on windows, a non-blocking connect() that fails does not come up as writable.
+                  Instead, it is added to the error set. See http://www.mail-archive.com/openssl-users@openssl.org/msg58500.html
+               */
+               FD_SET (sd, &(SelectData.fderrors));
+               #endif
+
+               if (SelectData.maxsocket < sd)
+                       SelectData.maxsocket = sd;
+       }
+
+
+       { // read and write the sockets
+               //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
+               //timeval tv = Quantum;
+               SelectData.tv = Quantum;
+               int s = SelectData._Select();
+               //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0);
+               //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
+               //int s = SelectData.nSockets;
+               if (s > 0) {
+                       /* Changed 01Jun07. We used to handle the Loop-breaker right here.
+                        * Now we do it AFTER all the regular descriptors. There's an
+                        * incredibly important and subtle reason for this. Code on
+                        * loop breakers is sometimes used to cause the reactor core to
+                        * cycle (for example, to allow outbound network buffers to drain).
+                        * If a loop-breaker handler reschedules itself (say, after determining
+                        * that the write buffers are still too full), then it will execute
+                        * IMMEDIATELY if _ReadLoopBreaker is done here instead of after
+                        * the other descriptors are processed. That defeats the whole purpose.
+                        */
+                       for (i=0; i < Descriptors.size(); i++) {
+                               EventableDescriptor *ed = Descriptors[i];
+                               assert (ed);
+                               int sd = ed->GetSocket();
+                               if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
+                                       continue;
+                               assert (sd != INVALID_SOCKET);
+
+                               if (FD_ISSET (sd, &(SelectData.fdwrites)))
+                                       ed->Write();
+                               if (FD_ISSET (sd, &(SelectData.fdreads)))
+                                       ed->Read();
+                               if (FD_ISSET (sd, &(SelectData.fderrors)))
+                                       ed->HandleError();
+                       }
+
+                       if (FD_ISSET (LoopBreakerReader, &(SelectData.fdreads)))
+                               _ReadLoopBreaker();
+               }
+               else if (s < 0) {
+                       // select can fail on error in a handful of ways.
+                       // If this happens, then wait for a little while to avoid busy-looping.
+                       // If the error was EINTR, we probably caught SIGCHLD or something,
+                       // so keep the wait short.
+                       timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
+                       EmSelect (0, NULL, NULL, NULL, &tv);
+               }
+       }
+
+
+       { // dispatch heartbeats
+               if (gCurrentLoopTime >= NextHeartbeatTime) {
+                       NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
+
+                       for (i=0; i < Descriptors.size(); i++) {
+                               EventableDescriptor *ed = Descriptors[i];
+                               assert (ed);
+                               ed->Heartbeat();
+                       }
+               }
+       }
+
+       { // cleanup dying sockets
+               // vector::pop_back works in constant time.
+               int i, j;
+               int nSockets = Descriptors.size();
+               for (i=0, j=0; i < nSockets; i++) {
+                       EventableDescriptor *ed = Descriptors[i];
+                       assert (ed);
+                       if (ed->ShouldDelete())
+                               delete ed;
+                       else
+                               Descriptors [j++] = ed;
+               }
+               while ((size_t)j < Descriptors.size())
+                       Descriptors.pop_back();
+
+       }
+
+       return true;
+}
+
+
+/********************************
+EventMachine_t::_ReadLoopBreaker
+********************************/
+
+void EventMachine_t::_ReadLoopBreaker()
+{
+       /* The loop breaker has selected readable.
+        * Read it ONCE (it may block if we try to read it twice)
+        * and send a loop-break event back to user code.
+        */
+       char buffer [1024];
+       read (LoopBreakerReader, buffer, sizeof(buffer));
+       if (EventCallback)
+               (*EventCallback)(NULL, EM_LOOPBREAK_SIGNAL, "", 0);
+}
+
+
+/**************************
+EventMachine_t::_RunTimers
+**************************/
+
+bool EventMachine_t::_RunTimers()
+{
+       // These are caller-defined timer handlers.
+       // Return T/F to indicate whether we should continue the main loop.
+       // We rely on the fact that multimaps sort by their keys to avoid
+       // inspecting the whole list every time we come here.
+       // Just keep inspecting and processing the list head until we hit
+       // one that hasn't expired yet.
+
+       while (true) {
+               multimap<Int64,Timer_t>::iterator i = Timers.begin();
+               if (i == Timers.end())
+                       break;
+               if (i->first > gCurrentLoopTime)
+                       break;
+               if (EventCallback)
+                       (*EventCallback) (NULL, EM_TIMER_FIRED, NULL, i->second.GetBinding());
+               Timers.erase (i);
+       }
+       return true;
+}
+
+
+
+/***********************************
+EventMachine_t::InstallOneshotTimer
+***********************************/
+
+const unsigned long EventMachine_t::InstallOneshotTimer (int milliseconds)
+{
+       if (Timers.size() > MaxOutstandingTimers)
+               return false;
+       // Don't use the global loop-time variable here, because we might
+       // get called before the main event machine is running.
+
+       #ifdef OS_UNIX
+       struct timeval tv;
+       gettimeofday (&tv, NULL);
+       Int64 fire_at = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
+       fire_at += ((Int64)milliseconds) * 1000LL;
+       #endif
+
+       #ifdef OS_WIN32
+       unsigned tick = GetTickCount();
+       if (tick < gLastTickCount)
+               gTickCountTickover += 1;
+       gLastTickCount = tick;
+
+       Int64 fire_at = ((Int64)gTickCountTickover << 32) + (Int64)tick;
+       fire_at += (Int64)milliseconds;
+       #endif
+
+       Timer_t t;
+       #ifndef HAVE_MAKE_PAIR
+       multimap<Int64,Timer_t>::iterator i = Timers.insert (multimap<Int64,Timer_t>::value_type (fire_at, t));
+       #else
+       multimap<Int64,Timer_t>::iterator i = Timers.insert (make_pair (fire_at, t));
+       #endif
+       return i->second.GetBinding();
+}
+
+
+/*******************************
+EventMachine_t::ConnectToServer
+*******************************/
+
+const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
+{
+       /* We want to spend no more than a few seconds waiting for a connection
+        * to a remote host. So we use a nonblocking connect.
+        * Linux disobeys the usual rules for nonblocking connects.
+        * Per Stevens (UNP p.410), you expect a nonblocking connect to select
+        * both readable and writable on error, and not to return EINPROGRESS
+        * if the connect can be fulfilled immediately. Linux violates both
+        * of these expectations.
+        * Any kind of nonblocking connect on Linux returns EINPROGRESS.
+        * The socket will then return writable when the disposition of the
+        * connect is known, but it will not also be readable in case of
+        * error! Weirdly, it will be readable in case there is data to read!!!
+        * (Which can happen with protocols like SSH and SMTP.)
+        * I suppose if you were so inclined you could consider this logical,
+        * but it's not the way Unix has historically done it.
+        * So we ignore the readable flag and read getsockopt to see if there
+        * was an error connecting. A select timeout works as expected.
+        * In regard to getsockopt: Linux does the Berkeley-style thing,
+        * not the Solaris-style, and returns zero with the error code in
+        * the error parameter.
+        * Return the binding-text of the newly-created pending connection,
+        * or NULL if there was a problem.
+        */
+
+       if (!server || !*server || !port)
+               throw std::runtime_error ("invalid server or port");
+
+       int family, bind_size;
+       struct sockaddr bind_as, *bind_as_ptr = name2address (server, port, &family, &bind_size);
+       if (!bind_as_ptr)
+               throw std::runtime_error ("unable to resolve server address");
+       bind_as = *bind_as_ptr; // copy because name2address points to a static
+
+       int sd = socket (family, SOCK_STREAM, 0);
+       if (sd == INVALID_SOCKET)
+               throw std::runtime_error ("unable to create new socket");
+
+       /*
+       sockaddr_in pin;
+       unsigned long HostAddr;
+
+       HostAddr = inet_addr (server);
+       if (HostAddr == INADDR_NONE) {
+               hostent *hp = gethostbyname ((char*)server); // Windows requires (char*)
+               if (!hp) {
+                       // TODO: This gives the caller a fatal error. Not good.
+                       // They can respond by catching RuntimeError (blecch).
+                       // Possibly we need to fire an unbind event and provide
+                       // a status code so user code can detect the cause of the
+                       // failure.
+                       return NULL;
+               }
+               HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
+       }
+
+       memset (&pin, 0, sizeof(pin));
+       pin.sin_family = AF_INET;
+       pin.sin_addr.s_addr = HostAddr;
+       pin.sin_port = htons (port);
+
+       int sd = socket (AF_INET, SOCK_STREAM, 0);
+       if (sd == INVALID_SOCKET)
+               return NULL;
+       */
+
+       // From here on, ALL error returns must close the socket.
+       // Set the new socket nonblocking.
+       if (!SetSocketNonblocking (sd)) {
+               closesocket (sd);
+               throw std::runtime_error ("unable to set socket as non-blocking");
+       }
+       // Disable slow-start (Nagle algorithm).
+       int one = 1;
+       setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
+       // Set reuseaddr to improve performance on restarts
+       setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one));
+
+       if (bind_addr) {
+               int bind_to_size, bind_to_family;
+               struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size);
+               if (!bind_to) {
+                       closesocket (sd);
+                       throw std::runtime_error ("invalid bind address");
+               }
+               if (bind (sd, bind_to, bind_to_size) < 0) {
+                       closesocket (sd);
+                       throw std::runtime_error ("couldn't bind to address");
+               }
+       }
+
+       unsigned long out = NULL;
+
+       #ifdef OS_UNIX
+       //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
+       if (connect (sd, &bind_as, bind_size) == 0) {
+               // This is a connect success, which Linux appears
+               // never to give when the socket is nonblocking,
+               // even if the connection is intramachine or to
+               // localhost.
+
+               /* Changed this branch 08Aug06. Evidently some kernels
+                * (FreeBSD for example) will actually return success from
+                * a nonblocking connect. This is a pretty simple case,
+                * just set up the new connection and clear the pending flag.
+                * Thanks to Chris Ochs for helping track this down.
+                * This branch never gets taken on Linux or (oddly) OSX.
+                * The original behavior was to throw an unimplemented,
+                * which the user saw as a fatal exception. Very unfriendly.
+                *
+                * Tweaked 10Aug06. Even though the connect disposition is
+                * known, we still set the connect-pending flag. That way
+                * some needed initialization will happen in the ConnectionDescriptor.
+                * (To wit, the ConnectionCompleted event gets sent to the client.)
+                */
+               ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+               if (!cd)
+                       throw std::runtime_error ("no connection allocated");
+               cd->SetConnectPending (true);
+               Add (cd);
+               out = cd->GetBinding();
+       }
+       else if (errno == EINPROGRESS) {
+               // Errno will generally always be EINPROGRESS, but on Linux
+               // we have to look at getsockopt to be sure what really happened.
+               int error;
+               socklen_t len;
+               len = sizeof(error);
+               int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
+               if ((o == 0) && (error == 0)) {
+                       // Here, there's no disposition.
+                       // Put the connection on the stack and wait for it to complete
+                       // or time out.
+                       ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+                       if (!cd)
+                               throw std::runtime_error ("no connection allocated");
+                       cd->SetConnectPending (true);
+                       Add (cd);
+                       out = cd->GetBinding();
+               }
+               else {
+                       /* This could be connection refused or some such thing.
+                        * We will come here on Linux if a localhost connection fails.
+                        * Changed 16Jul06: Originally this branch was a no-op, and
+                        * we'd drop down to the end of the method, close the socket,
+                        * and return NULL, which would cause the caller to GET A
+                        * FATAL EXCEPTION. Now we keep the socket around but schedule an
+                        * immediate close on it, so the caller will get a close-event
+                        * scheduled on it. This was only an issue for localhost connections
+                        * to non-listening ports. We may eventually need to revise this
+                        * revised behavior, in case it causes problems like making it hard
+                        * for people to know that a failure occurred.
+                        */
+                       ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+                       if (!cd)
+                               throw std::runtime_error ("no connection allocated");
+                       cd->ScheduleClose (false);
+                       Add (cd);
+                       out = cd->GetBinding();
+               }
+       }
+       else {
+               // The error from connect was something other then EINPROGRESS.
+       }
+       #endif
+
+       #ifdef OS_WIN32
+       //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
+       if (connect (sd, &bind_as, bind_size) == 0) {
+               // This is a connect success, which Windows appears
+               // never to give when the socket is nonblocking,
+               // even if the connection is intramachine or to
+               // localhost.
+               throw std::runtime_error ("unimplemented");
+       }
+       else if (WSAGetLastError() == WSAEWOULDBLOCK) {
+               // Here, there's no disposition.
+               // Windows appears not to surface refused connections or
+               // such stuff at this point.
+               // Put the connection on the stack and wait for it to complete
+               // or time out.
+               ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+               if (!cd)
+                       throw std::runtime_error ("no connection allocated");
+               cd->SetConnectPending (true);
+               Add (cd);
+               out = cd->GetBinding();
+       }
+       else {
+               // The error from connect was something other then WSAEWOULDBLOCK.
+       }
+
+       #endif
+
+       if (!out)
+               closesocket (sd);
+       return out;
+}
+
+/***********************************
+EventMachine_t::ConnectToUnixServer
+***********************************/
+
+const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
+{
+       /* Connect to a Unix-domain server, which by definition is running
+        * on the same host.
+        * There is no meaningful implementation on Windows.
+        * There's no need to do a nonblocking connect, since the connection
+        * is always local and can always be fulfilled immediately.
+        */
+
+       #ifdef OS_WIN32
+       throw std::runtime_error ("unix-domain connection unavailable on this platform");
+       return NULL;
+       #endif
+
+       // The whole rest of this function is only compiled on Unix systems.
+       #ifdef OS_UNIX
+
+       unsigned long out = NULL;
+
+       if (!server || !*server)
+               return NULL;
+
+       sockaddr_un pun;
+       memset (&pun, 0, sizeof(pun));
+       pun.sun_family = AF_LOCAL;
+
+       // You ordinarily expect the server name field to be at least 1024 bytes long,
+       // but on Linux it can be MUCH shorter.
+       if (strlen(server) >= sizeof(pun.sun_path))
+               throw std::runtime_error ("unix-domain server name is too long");
+
+
+       strcpy (pun.sun_path, server);
+
+       int fd = socket (AF_LOCAL, SOCK_STREAM, 0);
+       if (fd == INVALID_SOCKET)
+               return NULL;
+
+       // From here on, ALL error returns must close the socket.
+       // NOTE: At this point, the socket is still a blocking socket.
+       if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) {
+               closesocket (fd);
+               return NULL;
+       }
+
+       // Set the newly-connected socket nonblocking.
+       if (!SetSocketNonblocking (fd)) {
+               closesocket (fd);
+               return NULL;
+       }
+
+       // Set up a connection descriptor and add it to the event-machine.
+       // Observe, even though we know the connection status is connect-success,
+       // we still set the "pending" flag, so some needed initializations take
+       // place.
+       ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
+       if (!cd)
+               throw std::runtime_error ("no connection allocated");
+       cd->SetConnectPending (true);
+       Add (cd);
+       out = cd->GetBinding();
+
+       if (!out)
+               closesocket (fd);
+
+       return out;
+       #endif
+}
+
+/************************
+EventMachine_t::AttachFD
+************************/
+
+const unsigned long EventMachine_t::AttachFD (int fd, bool watch_mode)
+{
+       #ifdef OS_UNIX
+       if (fcntl(fd, F_GETFL, 0) < 0)
+               throw std::runtime_error ("invalid file descriptor");
+       #endif
+
+       #ifdef OS_WIN32
+       // TODO: add better check for invalid file descriptors (see ioctlsocket or getsockopt)
+       if (fd == INVALID_SOCKET)
+               throw std::runtime_error ("invalid file descriptor");
+       #endif
+
+       {// Check for duplicate descriptors
+               size_t i;
+               for (i = 0; i < Descriptors.size(); i++) {
+                       EventableDescriptor *ed = Descriptors[i];
+                       assert (ed);
+                       if (ed->GetSocket() == fd)
+                               throw std::runtime_error ("adding existing descriptor");
+               }
+
+               for (i = 0; i < NewDescriptors.size(); i++) {
+                       EventableDescriptor *ed = NewDescriptors[i];
+                       assert (ed);
+                       if (ed->GetSocket() == fd)
+                               throw std::runtime_error ("adding existing new descriptor");
+               }
+       }
+
+       if (!watch_mode)
+               SetSocketNonblocking(fd);
+
+       ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
+       if (!cd)
+               throw std::runtime_error ("no connection allocated");
+
+       cd->SetWatchOnly(watch_mode);
+       cd->SetConnectPending (false);
+
+       Add (cd);
+
+       const unsigned long out = cd->GetBinding();
+       return out;
+}
+
+/************************
+EventMachine_t::DetachFD
+************************/
+
+int EventMachine_t::DetachFD (EventableDescriptor *ed)
+{
+       if (!ed)
+               throw std::runtime_error ("detaching bad descriptor");
+
+       int fd = ed->GetSocket();
+
+       #ifdef HAVE_EPOLL
+       if (bEpoll) {
+               if (ed->GetSocket() != INVALID_SOCKET) {
+                       assert (epfd != -1);
+                       int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
+                       // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
+                       if (e && (errno != ENOENT) && (errno != EBADF)) {
+                               char buf [200];
+                               snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
+                               throw std::runtime_error (buf);
+                       }
+               }
+       }
+       #endif
+
+       #ifdef HAVE_KQUEUE
+       if (bKqueue) {
+               // remove any read/write events for this fd
+               struct kevent k;
+               EV_SET (&k, ed->GetSocket(), EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, ed);
+               int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+               if (t < 0 && (errno != ENOENT) && (errno != EBADF)) {
+                       char buf [200];
+                       snprintf (buf, sizeof(buf)-1, "unable to delete kqueue event: %s", strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+       }
+       #endif
+
+       // Prevent the descriptor from being modified, in case DetachFD was called from a timer or next_tick
+       ModifiedDescriptors.erase (ed);
+
+       // Set MySocket = INVALID_SOCKET so ShouldDelete() is true (and the descriptor gets deleted and removed),
+       // and also to prevent anyone from calling close() on the detached fd
+       ed->SetSocketInvalid();
+
+       return fd;
+}
+
+/************
+name2address
+************/
+
+struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size)
+{
+       // THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed.
+       // Check the more-common cases first.
+       // Return NULL if no resolution.
+
+       static struct sockaddr_in in4;
+       #ifndef __CYGWIN__
+       static struct sockaddr_in6 in6;
+       #endif
+       struct hostent *hp;
+
+       if (!server || !*server)
+               server = "0.0.0.0";
+
+       memset (&in4, 0, sizeof(in4));
+       if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) {
+               if (family)
+                       *family = AF_INET;
+               if (bind_size)
+                       *bind_size = sizeof(in4);
+               in4.sin_family = AF_INET;
+               in4.sin_port = htons (port);
+               return (struct sockaddr*)&in4;
+       }
+
+       #if defined(OS_UNIX) && !defined(__CYGWIN__)
+       memset (&in6, 0, sizeof(in6));
+       if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) {
+               if (family)
+                       *family = AF_INET6;
+               if (bind_size)
+                       *bind_size = sizeof(in6);
+               in6.sin6_family = AF_INET6;
+               in6.sin6_port = htons (port);
+               return (struct sockaddr*)&in6;
+       }
+       #endif
+
+       #ifdef OS_WIN32
+       // TODO, must complete this branch. Windows doesn't have inet_pton.
+       // A possible approach is to make a getaddrinfo call with the supplied
+       // server address, constraining the hints to ipv6 and seeing if we
+       // get any addresses.
+       // For the time being, Ipv6 addresses aren't supported on Windows.
+       #endif
+
+       hp = gethostbyname ((char*)server); // Windows requires the cast.
+       if (hp) {
+               in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
+               if (family)
+                       *family = AF_INET;
+               if (bind_size)
+                       *bind_size = sizeof(in4);
+               in4.sin_family = AF_INET;
+               in4.sin_port = htons (port);
+               return (struct sockaddr*)&in4;
+       }
+
+       return NULL;
+}
+
+
+/*******************************
+EventMachine_t::CreateTcpServer
+*******************************/
+
+const unsigned long EventMachine_t::CreateTcpServer (const char *server, int port)
+{
+       /* Create a TCP-acceptor (server) socket and add it to the event machine.
+        * Return the binding of the new acceptor to the caller.
+        * This binding will be referenced when the new acceptor sends events
+        * to indicate accepted connections.
+        */
+
+
+       int family, bind_size;
+       struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
+       if (!bind_here)
+               return NULL;
+
+       unsigned long output_binding = NULL;
+
+       //struct sockaddr_in sin;
+
+       int sd_accept = socket (family, SOCK_STREAM, 0);
+       if (sd_accept == INVALID_SOCKET) {
+               goto fail;
+       }
+
+       /*
+       memset (&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = INADDR_ANY;
+       sin.sin_port = htons (port);
+
+       if (server && *server) {
+               sin.sin_addr.s_addr = inet_addr (server);
+               if (sin.sin_addr.s_addr == INADDR_NONE) {
+                       hostent *hp = gethostbyname ((char*)server); // Windows requires the cast.
+                       if (hp == NULL) {
+                               //__warning ("hostname not resolved: ", server);
+                               goto fail;
+                       }
+                       sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
+               }
+       }
+       */
+
+       { // set reuseaddr to improve performance on restarts.
+               int oval = 1;
+               if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
+                       //__warning ("setsockopt failed while creating listener","");
+                       goto fail;
+               }
+       }
+
+       { // set CLOEXEC. Only makes sense on Unix
+               #ifdef OS_UNIX
+               int cloexec = fcntl (sd_accept, F_GETFD, 0);
+               assert (cloexec >= 0);
+               cloexec |= FD_CLOEXEC;
+               fcntl (sd_accept, F_SETFD, cloexec);
+               #endif
+       }
+
+
+       //if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
+       if (bind (sd_accept, bind_here, bind_size)) {
+               //__warning ("binding failed");
+               goto fail;
+       }
+
+       if (listen (sd_accept, 100)) {
+               //__warning ("listen failed");
+               goto fail;
+       }
+
+       {
+               // Set the acceptor non-blocking.
+               // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
+               if (!SetSocketNonblocking (sd_accept)) {
+               //int val = fcntl (sd_accept, F_GETFL, 0);
+               //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
+                       goto fail;
+               }
+       }
+
+       { // Looking good.
+               AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
+               if (!ad)
+                       throw std::runtime_error ("unable to allocate acceptor");
+               Add (ad);
+               output_binding = ad->GetBinding();
+       }
+
+       return output_binding;
+
+       fail:
+       if (sd_accept != INVALID_SOCKET)
+               closesocket (sd_accept);
+       return NULL;
+}
+
+
+/**********************************
+EventMachine_t::OpenDatagramSocket
+**********************************/
+
+const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int port)
+{
+       unsigned long output_binding = NULL;
+
+       int sd = socket (AF_INET, SOCK_DGRAM, 0);
+       if (sd == INVALID_SOCKET)
+               goto fail;
+       // from here on, early returns must close the socket!
+
+
+       struct sockaddr_in sin;
+       memset (&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons (port);
+
+
+       if (address && *address) {
+               sin.sin_addr.s_addr = inet_addr (address);
+               if (sin.sin_addr.s_addr == INADDR_NONE) {
+                       hostent *hp = gethostbyname ((char*)address); // Windows requires the cast.
+                       if (hp == NULL)
+                               goto fail;
+                       sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
+               }
+       }
+       else
+               sin.sin_addr.s_addr = htonl (INADDR_ANY);
+
+
+       // Set the new socket nonblocking.
+       {
+               if (!SetSocketNonblocking (sd))
+               //int val = fcntl (sd, F_GETFL, 0);
+               //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1)
+                       goto fail;
+       }
+
+       if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0)
+               goto fail;
+
+       { // Looking good.
+               DatagramDescriptor *ds = new DatagramDescriptor (sd, this);
+               if (!ds)
+                       throw std::runtime_error ("unable to allocate datagram-socket");
+               Add (ds);
+               output_binding = ds->GetBinding();
+       }
+
+       return output_binding;
+
+       fail:
+       if (sd != INVALID_SOCKET)
+               closesocket (sd);
+       return NULL;
+}
+
+
+
+/*******************
+EventMachine_t::Add
+*******************/
+
+void EventMachine_t::Add (EventableDescriptor *ed)
+{
+       if (!ed)
+               throw std::runtime_error ("added bad descriptor");
+       ed->SetEventCallback (EventCallback);
+       NewDescriptors.push_back (ed);
+}
+
+
+/*******************************
+EventMachine_t::ArmKqueueWriter
+*******************************/
+
+void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
+{
+       #ifdef HAVE_KQUEUE
+       if (bKqueue) {
+               if (!ed)
+                       throw std::runtime_error ("added bad descriptor");
+               struct kevent k;
+               EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed);
+               int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+               if (t < 0) {
+                       char buf [200];
+                       snprintf (buf, sizeof(buf)-1, "arm kqueue writer failed on %d: %s", ed->GetSocket(), strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+       }
+       #endif
+}
+
+/*******************************
+EventMachine_t::ArmKqueueReader
+*******************************/
+
+void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
+{
+       #ifdef HAVE_KQUEUE
+       if (bKqueue) {
+               if (!ed)
+                       throw std::runtime_error ("added bad descriptor");
+               struct kevent k;
+               EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
+               int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+               if (t < 0) {
+                       char buf [200];
+                       snprintf (buf, sizeof(buf)-1, "arm kqueue reader failed on %d: %s", ed->GetSocket(), strerror(errno));
+                       throw std::runtime_error (buf);
+               }
+       }
+       #endif
+}
+
+/**********************************
+EventMachine_t::_AddNewDescriptors
+**********************************/
+
+void EventMachine_t::_AddNewDescriptors()
+{
+       /* Avoid adding descriptors to the main descriptor list
+        * while we're actually traversing the list.
+        * Any descriptors that are added as a result of processing timers
+        * or acceptors should go on a temporary queue and then added
+        * while we're not traversing the main list.
+        * Also, it (rarely) happens that a newly-created descriptor
+        * is immediately scheduled to close. It might be a good
+        * idea not to bother scheduling these for I/O but if
+        * we do that, we might bypass some important processing.
+        */
+
+       for (size_t i = 0; i < NewDescriptors.size(); i++) {
+               EventableDescriptor *ed = NewDescriptors[i];
+               if (ed == NULL)
+                       throw std::runtime_error ("adding bad descriptor");
+
+               #if HAVE_EPOLL
+               if (bEpoll) {
+                       assert (epfd != -1);
+                       int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent());
+                       if (e) {
+                               char buf [200];
+                               snprintf (buf, sizeof(buf)-1, "unable to add new descriptor: %s", strerror(errno));
+                               throw std::runtime_error (buf);
+                       }
+               }
+               #endif
+
+               #if HAVE_KQUEUE
+               /*
+               if (bKqueue) {
+                       // INCOMPLETE. Some descriptors don't want to be readable.
+                       assert (kqfd != -1);
+                       struct kevent k;
+                       EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
+                       int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+                       assert (t == 0);
+               }
+               */
+               #endif
+
+               Descriptors.push_back (ed);
+       }
+       NewDescriptors.clear();
+}
+
+
+/**********************************
+EventMachine_t::_ModifyDescriptors
+**********************************/
+
+void EventMachine_t::_ModifyDescriptors()
+{
+       /* For implementations which don't level check every descriptor on
+        * every pass through the machine, as select does.
+        * If we're not selecting, then descriptors need a way to signal to the
+        * machine that their readable or writable status has changed.
+        * That's what the ::Modify call is for. We do it this way to avoid
+        * modifying descriptors during the loop traversal, where it can easily
+        * happen that an object (like a UDP socket) gets data written on it by
+        * the application during #post_init. That would take place BEFORE the
+        * descriptor even gets added to the epoll descriptor, so the modify
+        * operation will crash messily.
+        * Another really messy possibility is for a descriptor to put itself
+        * on the Modified list, and then get deleted before we get here.
+        * Remember, deletes happen after the I/O traversal and before the
+        * next pass through here. So we have to make sure when we delete a
+        * descriptor to remove it from the Modified list.
+        */
+
+       #ifdef HAVE_EPOLL
+       if (bEpoll) {
+               set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
+               while (i != ModifiedDescriptors.end()) {
+                       assert (*i);
+                       _ModifyEpollEvent (*i);
+                       ++i;
+               }
+       }
+       #endif
+
+       ModifiedDescriptors.clear();
+}
+
+
+/**********************
+EventMachine_t::Modify
+**********************/
+
+void EventMachine_t::Modify (EventableDescriptor *ed)
+{
+       if (!ed)
+               throw std::runtime_error ("modified bad descriptor");
+       ModifiedDescriptors.insert (ed);
+}
+
+
+/***********************************
+EventMachine_t::_OpenFileForWriting
+***********************************/
+
+const unsigned long EventMachine_t::_OpenFileForWriting (const char *filename)
+{
+  /*
+        * Return the binding-text of the newly-opened file,
+        * or NULL if there was a problem.
+        */
+
+       if (!filename || !*filename)
+               return NULL;
+
+  int fd = open (filename, O_CREAT|O_TRUNC|O_WRONLY|O_NONBLOCK, 0644);
+  
+       FileStreamDescriptor *fsd = new FileStreamDescriptor (fd, this);
+  if (!fsd)
+       throw std::runtime_error ("no file-stream allocated");
+  Add (fsd);
+  return fsd->GetBinding();
+
+}
+
+
+/**************************************
+EventMachine_t::CreateUnixDomainServer
+**************************************/
+
+const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename)
+{
+       /* Create a UNIX-domain acceptor (server) socket and add it to the event machine.
+        * Return the binding of the new acceptor to the caller.
+        * This binding will be referenced when the new acceptor sends events
+        * to indicate accepted connections.
+        * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS.
+        */
+
+       #ifdef OS_WIN32
+       throw std::runtime_error ("unix-domain server unavailable on this platform");
+       #endif
+
+       // The whole rest of this function is only compiled on Unix systems.
+       #ifdef OS_UNIX
+       unsigned long output_binding = NULL;
+
+       struct sockaddr_un s_sun;
+
+       int sd_accept = socket (AF_LOCAL, SOCK_STREAM, 0);
+       if (sd_accept == INVALID_SOCKET) {
+               goto fail;
+       }
+
+       if (!filename || !*filename)
+               goto fail;
+       unlink (filename);
+
+       bzero (&s_sun, sizeof(s_sun));
+       s_sun.sun_family = AF_LOCAL;
+       strncpy (s_sun.sun_path, filename, sizeof(s_sun.sun_path)-1);
+
+       // don't bother with reuseaddr for a local socket.
+
+       { // set CLOEXEC. Only makes sense on Unix
+               #ifdef OS_UNIX
+               int cloexec = fcntl (sd_accept, F_GETFD, 0);
+               assert (cloexec >= 0);
+               cloexec |= FD_CLOEXEC;
+               fcntl (sd_accept, F_SETFD, cloexec);
+               #endif
+       }
+
+       if (bind (sd_accept, (struct sockaddr*)&s_sun, sizeof(s_sun))) {
+               //__warning ("binding failed");
+               goto fail;
+       }
+
+       if (listen (sd_accept, 100)) {
+               //__warning ("listen failed");
+               goto fail;
+       }
+
+       {
+               // Set the acceptor non-blocking.
+               // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
+               if (!SetSocketNonblocking (sd_accept)) {
+               //int val = fcntl (sd_accept, F_GETFL, 0);
+               //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
+                       goto fail;
+               }
+       }
+
+       { // Looking good.
+               AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
+               if (!ad)
+                       throw std::runtime_error ("unable to allocate acceptor");
+               Add (ad);
+               output_binding = ad->GetBinding();
+       }
+
+       return output_binding;
+
+       fail:
+       if (sd_accept != INVALID_SOCKET)
+               closesocket (sd_accept);
+       return NULL;
+       #endif // OS_UNIX
+}
+
+
+/*********************
+EventMachine_t::Popen
+*********************/
+#if OBSOLETE
+const char *EventMachine_t::Popen (const char *cmd, const char *mode)
+{
+       #ifdef OS_WIN32
+       throw std::runtime_error ("popen is currently unavailable on this platform");
+       #endif
+
+       // The whole rest of this function is only compiled on Unix systems.
+       // Eventually we need this functionality (or a full-duplex equivalent) on Windows.
+       #ifdef OS_UNIX
+       const char *output_binding = NULL;
+
+       FILE *fp = popen (cmd, mode);
+       if (!fp)
+               return NULL;
+
+       // From here, all early returns must pclose the stream.
+
+       // According to the pipe(2) manpage, descriptors returned from pipe have both
+       // CLOEXEC and NONBLOCK clear. Do NOT set CLOEXEC. DO set nonblocking.
+       if (!SetSocketNonblocking (fileno (fp))) {
+               pclose (fp);
+               return NULL;
+       }
+
+       { // Looking good.
+               PipeDescriptor *pd = new PipeDescriptor (fp, this);
+               if (!pd)
+                       throw std::runtime_error ("unable to allocate pipe");
+               Add (pd);
+               output_binding = pd->GetBinding();
+       }
+
+       return output_binding;
+       #endif
+}
+#endif // OBSOLETE
+
+/**************************
+EventMachine_t::Socketpair
+**************************/
+
+const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
+{
+       #ifdef OS_WIN32
+       throw std::runtime_error ("socketpair is currently unavailable on this platform");
+       #endif
+
+       // The whole rest of this function is only compiled on Unix systems.
+       // Eventually we need this functionality (or a full-duplex equivalent) on Windows.
+       #ifdef OS_UNIX
+       // Make sure the incoming array of command strings is sane.
+       if (!cmd_strings)
+               return NULL;
+       int j;
+       for (j=0; j < 100 && cmd_strings[j]; j++)
+               ;
+       if ((j==0) || (j==100))
+               return NULL;
+
+       unsigned long output_binding = NULL;
+
+       int sv[2];
+       if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
+               return NULL;
+       // from here, all early returns must close the pair of sockets.
+
+       // Set the parent side of the socketpair nonblocking.
+       // We don't care about the child side, and most child processes will expect their
+       // stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out.
+       // Obviously DON'T set CLOEXEC.
+       if (!SetSocketNonblocking (sv[0])) {
+               close (sv[0]);
+               close (sv[1]);
+               return NULL;
+       }
+
+       pid_t f = fork();
+       if (f > 0) {
+               close (sv[1]);
+               PipeDescriptor *pd = new PipeDescriptor (sv[0], f, this);
+               if (!pd)
+                       throw std::runtime_error ("unable to allocate pipe");
+               Add (pd);
+               output_binding = pd->GetBinding();
+       }
+       else if (f == 0) {
+               close (sv[0]);
+               dup2 (sv[1], STDIN_FILENO);
+               close (sv[1]);
+               dup2 (STDIN_FILENO, STDOUT_FILENO);
+               execvp (cmd_strings[0], cmd_strings+1);
+               exit (-1); // end the child process if the exec doesn't work.
+       }
+       else
+               throw std::runtime_error ("no fork");
+
+       return output_binding;
+       #endif
+}
+
+
+/****************************
+EventMachine_t::OpenKeyboard
+****************************/
+
+const unsigned long EventMachine_t::OpenKeyboard()
+{
+       KeyboardDescriptor *kd = new KeyboardDescriptor (this);
+       if (!kd)
+               throw std::runtime_error ("no keyboard-object allocated");
+       Add (kd);
+       return kd->GetBinding();
+}
+
+
+/**********************************
+EventMachine_t::GetConnectionCount
+**********************************/
+
+int EventMachine_t::GetConnectionCount ()
+{
+       return Descriptors.size() + NewDescriptors.size();
+}
+
+
+/************************
+EventMachine_t::WatchPid
+************************/
+
+const unsigned long EventMachine_t::WatchPid (int pid)
+{
+       #ifdef HAVE_KQUEUE
+       if (!bKqueue)
+               throw std::runtime_error("must enable kqueue");
+
+       struct kevent event;
+       int kqres;
+
+       EV_SET(&event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT | NOTE_FORK, 0, 0);
+
+       // Attempt to register the event
+       kqres = kevent(kqfd, &event, 1, NULL, 0, NULL);
+       if (kqres == -1) {
+               char errbuf[200];
+               sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
+               throw std::runtime_error(errbuf);
+       }
+       #endif
+
+       #ifdef HAVE_KQUEUE
+       Bindable_t* b = new Bindable_t();
+       Pids.insert(make_pair (pid, b));
+
+       return b->GetBinding();
+       #endif
+
+       throw std::runtime_error("no pid watching support on this system");
+}
+
+/**************************
+EventMachine_t::UnwatchPid
+**************************/
+
+void EventMachine_t::UnwatchPid (int pid)
+{
+       Bindable_t *b = Pids[pid];
+       assert(b);
+       Pids.erase(pid);
+
+       #ifdef HAVE_KQUEUE
+       struct kevent k;
+
+       EV_SET(&k, pid, EVFILT_PROC, EV_DELETE, 0, 0, 0);
+       /*int t =*/ kevent (kqfd, &k, 1, NULL, 0, NULL);
+       // t==-1 if the process already exited; ignore this for now
+       #endif
+
+       if (EventCallback)
+               (*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
+
+       delete b;
+}
+
+void EventMachine_t::UnwatchPid (const unsigned long sig)
+{
+       for(map<int, Bindable_t*>::iterator i=Pids.begin(); i != Pids.end(); i++)
+       {
+               if (i->second->GetBinding() == sig) {
+                       UnwatchPid (i->first);
+                       return;
+               }
+       }
+
+       throw std::runtime_error("attempted to remove invalid pid signature");
+}
+
+
+/*************************
+EventMachine_t::WatchFile
+*************************/
+
+const unsigned long EventMachine_t::WatchFile (const char *fpath)
+{
+       struct stat sb;
+       int sres;
+       int wd = -1;
+
+       sres = stat(fpath, &sb);
+
+       if (sres == -1) {
+               char errbuf[300];
+               sprintf(errbuf, "error registering file %s for watching: %s", fpath, strerror(errno));
+               throw std::runtime_error(errbuf);
+       }
+
+       #ifdef HAVE_INOTIFY
+       if (!inotify) {
+               inotify = new InotifyDescriptor(this);
+               assert (inotify);
+               Add(inotify);
+       }
+
+       wd = inotify_add_watch(inotify->GetSocket(), fpath, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
+       if (wd == -1) {
+               char errbuf[300];
+               sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
+               throw std::runtime_error(errbuf);
+       }
+       #endif
+
+       #ifdef HAVE_KQUEUE
+       if (!bKqueue)
+               throw std::runtime_error("must enable kqueue");
+
+       // With kqueue we have to open the file first and use the resulting fd to register for events
+       wd = open(fpath, O_RDONLY);
+       if (wd == -1) {
+               char errbuf[300];
+               sprintf(errbuf, "failed to open file %s for registering with kqueue: %s", fpath, strerror(errno));
+               throw std::runtime_error(errbuf);
+       }
+       _RegisterKqueueFileEvent(wd);
+       #endif
+
+       if (wd != -1) {
+               Bindable_t* b = new Bindable_t();
+               Files.insert(make_pair (wd, b));
+
+               return b->GetBinding();
+       }
+
+       throw std::runtime_error("no file watching support on this system"); // is this the right thing to do?
+}
+
+
+/***************************
+EventMachine_t::UnwatchFile
+***************************/
+
+void EventMachine_t::UnwatchFile (int wd)
+{
+       Bindable_t *b = Files[wd];
+       assert(b);
+       Files.erase(wd);
+
+       #ifdef HAVE_INOTIFY
+       inotify_rm_watch(inotify->GetSocket(), wd);
+       #elif HAVE_KQUEUE
+       // With kqueue, closing the monitored fd automatically clears all registered events for it
+       close(wd);
+       #endif
+
+       if (EventCallback)
+               (*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
+
+       delete b;
+}
+
+void EventMachine_t::UnwatchFile (const unsigned long sig)
+{
+       for(map<int, Bindable_t*>::iterator i=Files.begin(); i != Files.end(); i++)
+       {
+               if (i->second->GetBinding() == sig) {
+                       UnwatchFile (i->first);
+                       return;
+               }
+       }
+       throw std::runtime_error("attempted to remove invalid watch signature");
+}
+
+
+/***********************************
+EventMachine_t::_ReadInotify_Events
+************************************/
+
+void EventMachine_t::_ReadInotifyEvents()
+{
+       #ifdef HAVE_INOTIFY
+       struct inotify_event event;
+
+       assert(EventCallback);
+
+       while (read(inotify->GetSocket(), &event, INOTIFY_EVENT_SIZE) > 0) {
+               assert(event.len == 0);
+               if (event.mask & IN_MODIFY)
+                       (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
+               if (event.mask & IN_MOVE_SELF)
+                       (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
+               if (event.mask & IN_DELETE_SELF) {
+                       (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
+                       UnwatchFile ((int)event.wd);
+               }
+       }
+       #endif
+}
+
+
+/*************************************
+EventMachine_t::_HandleKqueuePidEvent
+*************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_HandleKqueuePidEvent(struct kevent *event)
+{
+       assert(EventCallback);
+
+       if (event->fflags & NOTE_FORK)
+               (*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "fork", 4);
+       if (event->fflags & NOTE_EXIT) {
+               (*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "exit", 4);
+               // stop watching the pid if it died
+               UnwatchPid ((int)event->ident);
+       }
+}
+#endif
+
+
+/**************************************
+EventMachine_t::_HandleKqueueFileEvent
+***************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_HandleKqueueFileEvent(struct kevent *event)
+{
+       assert(EventCallback);
+
+       if (event->fflags & NOTE_WRITE)
+               (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
+       if (event->fflags & NOTE_RENAME)
+               (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
+       if (event->fflags & NOTE_DELETE) {
+               (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
+               UnwatchFile ((int)event->ident);
+       }
+}
+#endif
+
+
+/****************************************
+EventMachine_t::_RegisterKqueueFileEvent
+*****************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_RegisterKqueueFileEvent(int fd)
+{
+       struct kevent newevent;
+       int kqres;
+
+       // Setup the event with our fd and proper flags
+       EV_SET(&newevent, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE, 0, 0);
+
+       // Attempt to register the event
+       kqres = kevent(kqfd, &newevent, 1, NULL, 0, NULL);
+       if (kqres == -1) {
+               char errbuf[200];
+               sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
+               close(fd);
+               throw std::runtime_error(errbuf);
+       }
+}
+#endif
+
+
+/************************************
+EventMachine_t::GetHeartbeatInterval
+*************************************/
+
+float EventMachine_t::GetHeartbeatInterval()
+{
+       return ((float)HeartbeatInterval / 1000000);
+}
+
+
+/************************************
+EventMachine_t::SetHeartbeatInterval
+*************************************/
+
+int EventMachine_t::SetHeartbeatInterval(float interval)
+{
+       int iv = (int)(interval * 1000000);
+       if (iv > 0) {
+               HeartbeatInterval = iv;
+               return 1;
+       }
+       return 0;
+}
+//#endif // OS_UNIX
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/em.h
new file mode 100644 (file)
index 0000000..c5d409a
--- /dev/null
@@ -0,0 +1,232 @@
+/*****************************************************************************
+
+$Id$
+
+File:     em.h
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+
+#ifdef OS_WIN32
+#include "emwin.h"
+#endif
+
+
+// THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.
+//#ifdef OS_UNIX
+
+#ifndef __EventMachine__H_
+#define __EventMachine__H_
+
+#ifdef BUILD_FOR_RUBY
+  #include <ruby.h>
+  #define EmSelect rb_thread_select
+
+  #if defined(HAVE_RBTRAP)
+    #include <rubysig.h>
+  #elif defined(HAVE_RB_THREAD_CHECK_INTS)
+    extern "C" {
+      void rb_enable_interrupt(void);
+      void rb_disable_interrupt(void);
+    }
+
+    #define TRAP_BEG rb_enable_interrupt()
+    #define TRAP_END do { rb_disable_interrupt(); rb_thread_check_ints(); } while(0)
+  #else
+    #define TRAP_BEG
+    #define TRAP_END
+  #endif
+
+  // 1.9.0 compat
+  #ifndef RUBY_UBF_IO
+    #define RUBY_UBF_IO RB_UBF_DFL
+  #endif
+#else
+  #define EmSelect select
+#endif
+
+
+#ifdef OS_UNIX
+typedef long long Int64;
+#endif
+#ifdef OS_WIN32
+typedef __int64 Int64;
+#endif
+
+extern Int64 gCurrentLoopTime;
+
+class EventableDescriptor;
+class InotifyDescriptor;
+
+
+/********************
+class EventMachine_t
+********************/
+
+class EventMachine_t
+{
+       public:
+               static int GetMaxTimerCount();
+               static void SetMaxTimerCount (int);
+
+       public:
+               EventMachine_t (void(*event_callback)(const unsigned long, int, const char*, const unsigned long));
+               virtual ~EventMachine_t();
+
+               void Run();
+               void ScheduleHalt();
+               void SignalLoopBreaker();
+               const unsigned long InstallOneshotTimer (int);
+               const unsigned long ConnectToServer (const char *, int, const char *, int);
+               const unsigned long ConnectToUnixServer (const char *);
+
+               const unsigned long CreateTcpServer (const char *, int);
+               const unsigned long OpenDatagramSocket (const char *, int);
+               const unsigned long CreateUnixDomainServer (const char*);
+               const unsigned long _OpenFileForWriting (const char*);
+               const unsigned long OpenKeyboard();
+               //const char *Popen (const char*, const char*);
+               const unsigned long Socketpair (char* const*);
+
+               void Add (EventableDescriptor*);
+               void Modify (EventableDescriptor*);
+
+               const unsigned long AttachFD (int, bool);
+               int DetachFD (EventableDescriptor*);
+
+               void ArmKqueueWriter (EventableDescriptor*);
+               void ArmKqueueReader (EventableDescriptor*);
+
+               void SetTimerQuantum (int);
+               static void SetuidString (const char*);
+               static int SetRlimitNofile (int);
+
+               pid_t SubprocessPid;
+               int SubprocessExitStatus;
+
+               int GetConnectionCount();
+               float GetHeartbeatInterval();
+               int SetHeartbeatInterval(float);
+
+               const unsigned long WatchFile (const char*);
+               void UnwatchFile (int);
+               void UnwatchFile (const unsigned long);
+
+               #ifdef HAVE_KQUEUE
+               void _HandleKqueueFileEvent (struct kevent*);
+               void _RegisterKqueueFileEvent(int);
+               #endif
+
+               const unsigned long WatchPid (int);
+               void UnwatchPid (int);
+               void UnwatchPid (const unsigned long);
+
+               #ifdef HAVE_KQUEUE
+               void _HandleKqueuePidEvent (struct kevent*);
+               #endif
+
+               // Temporary:
+               void _UseEpoll();
+               void _UseKqueue();
+
+               bool UsingKqueue() { return bKqueue; }
+               bool UsingEpoll() { return bEpoll; }
+
+       private:
+               bool _RunOnce();
+               bool _RunTimers();
+               void _UpdateTime();
+               void _AddNewDescriptors();
+               void _ModifyDescriptors();
+               void _InitializeLoopBreaker();
+
+               bool _RunSelectOnce();
+               bool _RunEpollOnce();
+               bool _RunKqueueOnce();
+
+               void _ModifyEpollEvent (EventableDescriptor*);
+
+       public:
+               void _ReadLoopBreaker();
+               void _ReadInotifyEvents();
+
+       private:
+               enum {
+                       MaxEpollDescriptors = 64*1024,
+                       MaxEvents = 4096
+               };
+               int HeartbeatInterval;
+               void (*EventCallback)(const unsigned long, int, const char*, const unsigned long);
+
+               class Timer_t: public Bindable_t {
+               };
+
+               multimap<Int64, Timer_t> Timers;
+               map<int, Bindable_t*> Files;
+               map<int, Bindable_t*> Pids;
+               vector<EventableDescriptor*> Descriptors;
+               vector<EventableDescriptor*> NewDescriptors;
+               set<EventableDescriptor*> ModifiedDescriptors;
+
+               Int64 NextHeartbeatTime;
+
+               int LoopBreakerReader;
+               int LoopBreakerWriter;
+               #ifdef OS_WIN32
+               struct sockaddr_in LoopBreakerTarget;
+               #endif
+
+               timeval Quantum;
+
+       private:
+               bool bEpoll;
+               int epfd; // Epoll file-descriptor
+               #ifdef HAVE_EPOLL
+               struct epoll_event epoll_events [MaxEvents];
+               #endif
+
+               bool bKqueue;
+               int kqfd; // Kqueue file-descriptor
+               #ifdef HAVE_KQUEUE
+               struct kevent Karray [MaxEvents];
+               #endif
+
+               InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
+};
+
+
+/*******************
+struct SelectData_t
+*******************/
+
+struct SelectData_t
+{
+       SelectData_t();
+
+       int _Select();
+
+       int maxsocket;
+       fd_set fdreads;
+       fd_set fdwrites;
+       fd_set fderrors;
+       timeval tv;
+       int nSockets;
+};
+
+
+
+#endif // __EventMachine__H_
+
+//#endif // OS_UNIX
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.cpp
new file mode 100644 (file)
index 0000000..181f001
--- /dev/null
@@ -0,0 +1,300 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     emwin.cpp\r
+Date:     05May06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+// THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY\r
+// INCOMPLETE AND DISABLED FOR NOW.\r
+#ifdef xOS_WIN32\r
+\r
+#include "project.h"\r
+\r
+\r
+// Keep a global variable floating around\r
+// with the current loop time as set by the Event Machine.\r
+// This avoids the need for frequent expensive calls to time(NULL);\r
+time_t gCurrentLoopTime;\r
+\r
+\r
+/******************************\r
+EventMachine_t::EventMachine_t\r
+******************************/\r
+\r
+EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const char*, int)):\r
+       EventCallback (event_callback),\r
+       NextHeartbeatTime (0)\r
+{\r
+       gTerminateSignalReceived = false;\r
+       Iocp = NULL;\r
+}\r
+\r
+\r
+/*******************************\r
+EventMachine_t::~EventMachine_t\r
+*******************************/\r
+\r
+EventMachine_t::~EventMachine_t()\r
+{\r
+       cerr << "EM __dt\n";\r
+       if (Iocp)\r
+               CloseHandle (Iocp);\r
+}\r
+\r
+\r
+/****************************\r
+EventMachine_t::ScheduleHalt\r
+****************************/\r
+\r
+void EventMachine_t::ScheduleHalt()\r
+{\r
+  /* This is how we stop the machine.\r
+   * This can be called by clients. Signal handlers will probably\r
+   * set the global flag.\r
+   * For now this means there can only be one EventMachine ever running at a time.\r
+   */\r
+       gTerminateSignalReceived = true;\r
+}\r
+\r
+\r
+\r
+/*******************\r
+EventMachine_t::Run\r
+*******************/\r
+\r
+void EventMachine_t::Run()\r
+{\r
+       HookControlC (true);\r
+\r
+       Iocp = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 0);\r
+       if (Iocp == NULL)\r
+               throw std::runtime_error ("no completion port");\r
+\r
+\r
+       DWORD nBytes, nCompletionKey;\r
+       LPOVERLAPPED Overlapped;\r
+\r
+       do {\r
+               gCurrentLoopTime = time(NULL);\r
+               // Have some kind of strategy that will dequeue maybe up to 10 completions \r
+               // without running the timers as long as they are available immediately.\r
+               // Otherwise in a busy server we're calling them every time through the loop.\r
+               if (!_RunTimers())\r
+                       break;\r
+               if (GetQueuedCompletionStatus (Iocp, &nBytes, &nCompletionKey, &Overlapped, 1000)) {\r
+               }\r
+               cerr << "+";\r
+       } while (!gTerminateSignalReceived);\r
+\r
+\r
+       /*\r
+       while (true) {\r
+               gCurrentLoopTime = time(NULL);\r
+               if (!_RunTimers())\r
+                       break;\r
+               _AddNewDescriptors();\r
+               if (!_RunOnce())\r
+                       break;\r
+               if (gTerminateSignalReceived)\r
+                       break;\r
+       }\r
+       */\r
+\r
+       HookControlC (false);\r
+}\r
+\r
+\r
+/**************************\r
+EventMachine_t::_RunTimers\r
+**************************/\r
+\r
+bool EventMachine_t::_RunTimers()\r
+{\r
+       // These are caller-defined timer handlers.\r
+       // Return T/F to indicate whether we should continue the main loop.\r
+       // We rely on the fact that multimaps sort by their keys to avoid\r
+       // inspecting the whole list every time we come here.\r
+       // Just keep inspecting and processing the list head until we hit\r
+       // one that hasn't expired yet.\r
+\r
+       while (true) {\r
+               multimap<time_t,Timer_t>::iterator i = Timers.begin();\r
+               if (i == Timers.end())\r
+                       break;\r
+               if (i->first > gCurrentLoopTime)\r
+                       break;\r
+               if (EventCallback)\r
+                       (*EventCallback) (NULL, EM_TIMER_FIRED, NULL, i->second.GetBinding());\r
+               Timers.erase (i);\r
+       }\r
+       return true;\r
+}\r
+\r
+\r
+/***********************************\r
+EventMachine_t::InstallOneshotTimer\r
+***********************************/\r
+\r
+const char *EventMachine_t::InstallOneshotTimer (int seconds)\r
+{\r
+       if (Timers.size() > MaxOutstandingTimers)\r
+               return false;\r
+       // Don't use the global loop-time variable here, because we might\r
+       // get called before the main event machine is running.\r
+\r
+       Timer_t t;\r
+       Timers.insert (make_pair (time(NULL) + seconds, t));\r
+       return t.GetBinding();\r
+}\r
+\r
+\r
+/**********************************\r
+EventMachine_t::OpenDatagramSocket\r
+**********************************/\r
+\r
+const char *EventMachine_t::OpenDatagramSocket (const char *address, int port)\r
+{\r
+       cerr << "OPEN DATAGRAM SOCKET\n";\r
+       return "Unimplemented";\r
+}\r
+\r
+\r
+/*******************************\r
+EventMachine_t::CreateTcpServer\r
+*******************************/\r
+\r
+const char *EventMachine_t::CreateTcpServer (const char *server, int port)\r
+{\r
+       /* Create a TCP-acceptor (server) socket and add it to the event machine.\r
+        * Return the binding of the new acceptor to the caller.\r
+        * This binding will be referenced when the new acceptor sends events\r
+        * to indicate accepted connections.\r
+        */\r
+\r
+       const char *output_binding = NULL;\r
+\r
+       struct sockaddr_in sin;\r
+\r
+       SOCKET sd_accept = socket (AF_INET, SOCK_STREAM, 0);\r
+       if (sd_accept == INVALID_SOCKET) {\r
+               goto fail;\r
+       }\r
+\r
+       memset (&sin, 0, sizeof(sin));\r
+       sin.sin_family = AF_INET;\r
+       sin.sin_addr.s_addr = INADDR_ANY;\r
+       sin.sin_port = htons (port);\r
+\r
+       if (server && *server) {\r
+               sin.sin_addr.s_addr = inet_addr (server);\r
+               if (sin.sin_addr.s_addr == INADDR_NONE) {\r
+                       hostent *hp = gethostbyname (server);\r
+                       if (hp == NULL) {\r
+                               //__warning ("hostname not resolved: ", server);\r
+                               goto fail;\r
+                       }\r
+                       sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;\r
+               }\r
+       }\r
+\r
+\r
+       // No need to set reuseaddr on Windows.\r
+\r
+\r
+       if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {\r
+               //__warning ("binding failed");\r
+               goto fail;\r
+       }\r
+\r
+       if (listen (sd_accept, 100)) {\r
+               //__warning ("listen failed");\r
+               goto fail;\r
+       }\r
+\r
+       { // Looking good.\r
+               AcceptorDescriptor *ad = new AcceptorDescriptor (this, sd_accept);\r
+               if (!ad)\r
+                       throw std::runtime_error ("unable to allocate acceptor");\r
+               Add (ad);\r
+               output_binding = ad->GetBinding();\r
+\r
+               CreateIoCompletionPort ((HANDLE)sd_accept, Iocp, NULL, 0);\r
+               SOCKET sd = socket (AF_INET, SOCK_STREAM, 0);\r
+               CreateIoCompletionPort ((HANDLE)sd, Iocp, NULL, 0);\r
+               AcceptEx (sd_accept, sd, \r
+       }\r
+\r
+       return output_binding;\r
+\r
+       fail:\r
+       if (sd_accept != INVALID_SOCKET)\r
+               closesocket (sd_accept);\r
+       return NULL;\r
+}\r
+\r
+\r
+/*******************************\r
+EventMachine_t::ConnectToServer\r
+*******************************/\r
+\r
+const char *EventMachine_t::ConnectToServer (const char *server, int port)\r
+{\r
+       if (!server || !*server || !port)\r
+               return NULL;\r
+\r
+       sockaddr_in pin;\r
+       unsigned long HostAddr;\r
+\r
+       HostAddr = inet_addr (server);\r
+       if (HostAddr == INADDR_NONE) {\r
+               hostent *hp = gethostbyname (server);\r
+               if (!hp)\r
+                       return NULL;\r
+               HostAddr = ((in_addr*)(hp->h_addr))->s_addr;\r
+       }\r
+\r
+       memset (&pin, 0, sizeof(pin));\r
+       pin.sin_family = AF_INET;\r
+       pin.sin_addr.s_addr = HostAddr;\r
+       pin.sin_port = htons (port);\r
+\r
+       int sd = socket (AF_INET, SOCK_STREAM, 0);\r
+       if (sd == INVALID_SOCKET)\r
+               return NULL;\r
+\r
+\r
+       LPOVERLAPPED olap = (LPOVERLAPPED) calloc (1, sizeof (OVERLAPPED));\r
+       cerr << "I'm dying now\n";\r
+       throw runtime_error ("UNIMPLEMENTED!!!\n");\r
+\r
+}\r
+\r
+\r
+\r
+/*******************\r
+EventMachine_t::Add\r
+*******************/\r
+\r
+void EventMachine_t::Add (EventableDescriptor *ed)\r
+{\r
+       cerr << "ADD\n";\r
+}\r
+\r
+\r
+\r
+#endif // OS_WIN32\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/emwin.h
new file mode 100644 (file)
index 0000000..1585f93
--- /dev/null
@@ -0,0 +1,94 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     emwin.h\r
+Date:     05May06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+// THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY.\r
+// INCOMPLETE AND DISABLED FOR NOW.\r
+#ifdef xOS_WIN32\r
+\r
+#ifndef __EventMachine__H_\r
+#define __EventMachine__H_\r
+\r
+\r
+extern time_t gCurrentLoopTime;\r
+\r
+class EventableDescriptor;\r
+\r
+\r
+/********************\r
+class EventMachine_t\r
+********************/\r
+\r
+class EventMachine_t\r
+{\r
+       public:\r
+               EventMachine_t (void(*event_callback)(const char*, int, const char*, int));\r
+               virtual ~EventMachine_t();\r
+\r
+               void Run();\r
+               void ScheduleHalt();\r
+               const char *InstallOneshotTimer (int);\r
+               const char *ConnectToServer (const char *, int);\r
+               const char *CreateTcpServer (const char *, int);\r
+               const char *OpenDatagramSocket (const char *, int);\r
+\r
+               void Add (EventableDescriptor*);\r
+\r
+       public:\r
+               enum { // Event names\r
+                       TIMER_FIRED = 100,\r
+                       CONNECTION_READ = 101,\r
+                       CONNECTION_UNBOUND = 102,\r
+                       CONNECTION_ACCEPTED = 103,\r
+                       CONNECTION_COMPLETED = 104,\r
+                       LOOPBREAK_SIGNAL = 105\r
+               };\r
+\r
+       private:\r
+               HANDLE Iocp;\r
+\r
+       private:\r
+               bool _RunOnce();\r
+               bool _RunTimers();\r
+               void _AddNewDescriptors();\r
+\r
+       private:\r
+               enum {\r
+                       MaxOutstandingTimers = 40,\r
+                       HeartbeatInterval = 2\r
+               };\r
+               void (*EventCallback)(const char*, int, const char*, int);\r
+\r
+               class Timer_t: public Bindable_t {\r
+               };\r
+\r
+               multimap<time_t, Timer_t> Timers;\r
+               vector<EventableDescriptor*> Descriptors;\r
+               vector<EventableDescriptor*> NewDescriptors;\r
+\r
+               time_t NextHeartbeatTime;\r
+};\r
+\r
+\r
+\r
+\r
+#endif // __EventMachine__H_\r
+\r
+#endif // OS_WIN32\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.cpp
new file mode 100644 (file)
index 0000000..f82c80e
--- /dev/null
@@ -0,0 +1,26 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     epoll.cpp\r
+Date:     06Jun07\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifdef HAVE_EPOLL\r
+\r
+#include "project.h"\r
+\r
+#endif // HAVE_EPOLL\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/epoll.h
new file mode 100644 (file)
index 0000000..8d58166
--- /dev/null
@@ -0,0 +1,25 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     epoll.h\r
+Date:     06Jun07\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifdef HAVE_EPOLL\r
+\r
+\r
+#endif // HAVE_EPOLL\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine.h
new file mode 100644 (file)
index 0000000..f7baa21
--- /dev/null
@@ -0,0 +1,122 @@
+/*****************************************************************************
+
+$Id$
+
+File:     eventmachine.h
+Date:     15Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#ifndef __EVMA_EventMachine__H_
+#define __EVMA_EventMachine__H_
+
+#if __cplusplus
+extern "C" {
+#endif
+
+       enum { // Event names
+               EM_TIMER_FIRED = 100,
+               EM_CONNECTION_READ = 101,
+               EM_CONNECTION_UNBOUND = 102,
+               EM_CONNECTION_ACCEPTED = 103,
+               EM_CONNECTION_COMPLETED = 104,
+               EM_LOOPBREAK_SIGNAL = 105,
+               EM_CONNECTION_NOTIFY_READABLE = 106,
+               EM_CONNECTION_NOTIFY_WRITABLE = 107,
+               EM_SSL_HANDSHAKE_COMPLETED = 108,
+               EM_SSL_VERIFY = 109,
+               EM_PROXY_TARGET_UNBOUND = 110
+
+       };
+
+       void evma_initialize_library (void(*)(const unsigned long, int, const char*, const unsigned long));
+       void evma_run_machine();
+       void evma_release_library();
+       const unsigned long evma_install_oneshot_timer (int seconds);
+       const unsigned long evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
+       const unsigned long evma_connect_to_unix_server (const char *server);
+
+       const unsigned long evma_attach_fd (int file_descriptor, int watch_mode);
+       int evma_detach_fd (const unsigned long binding);
+       int evma_get_file_descriptor (const unsigned long binding);
+       int evma_is_notify_readable (const unsigned long binding);
+       void evma_set_notify_readable (const unsigned long binding, int mode);
+       int evma_is_notify_writable (const unsigned long binding);
+       void evma_set_notify_writable (const unsigned long binding, int mode);
+
+       int evma_pause(const unsigned long binding);
+       int evma_is_paused(const unsigned long binding);
+       int evma_resume(const unsigned long binding);
+
+       void evma_stop_tcp_server (const unsigned long signature);
+       const unsigned long evma_create_tcp_server (const char *address, int port);
+       const unsigned long evma_create_unix_domain_server (const char *filename);
+       const unsigned long evma_open_datagram_socket (const char *server, int port);
+       const unsigned long evma_open_keyboard();
+       void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer);
+       void evma_start_tls (const unsigned long binding);
+
+       #ifdef WITH_SSL
+       X509 *evma_get_peer_cert (const unsigned long binding);
+       void evma_accept_ssl_peer (const unsigned long binding);
+       #endif
+
+       int evma_get_peername (const unsigned long binding, struct sockaddr*);
+       int evma_get_sockname (const unsigned long binding, struct sockaddr*);
+       int evma_get_subprocess_pid (const unsigned long binding, pid_t*);
+       int evma_get_subprocess_status (const unsigned long binding, int*);
+       int evma_get_connection_count();
+       int evma_send_data_to_connection (const unsigned long binding, const char *data, int data_length);
+       int evma_send_datagram (const unsigned long binding, const char *data, int data_length, const char *address, int port);
+       float evma_get_comm_inactivity_timeout (const unsigned long binding);
+       int evma_set_comm_inactivity_timeout (const unsigned long binding, float value);
+       float evma_get_pending_connect_timeout (const unsigned long binding);
+       int evma_set_pending_connect_timeout (const unsigned long binding, float value);
+       int evma_get_outbound_data_size (const unsigned long binding);
+       int evma_send_file_data_to_connection (const unsigned long binding, const char *filename);
+
+       void evma_close_connection (const unsigned long binding, int after_writing);
+       int evma_report_connection_error_status (const unsigned long binding);
+       void evma_signal_loopbreak();
+       void evma_set_timer_quantum (int);
+       int evma_get_max_timer_count();
+       void evma_set_max_timer_count (int);
+       void evma_setuid_string (const char *username);
+       void evma_stop_machine();
+       float evma_get_heartbeat_interval();
+       int evma_set_heartbeat_interval(float);
+
+       const unsigned long evma__write_file (const char *filename);
+       const unsigned long evma_popen (char * const*cmd_strings);
+
+       const unsigned long evma_watch_filename (const char *fname);
+       void evma_unwatch_filename (const unsigned long);
+
+       const unsigned long evma_watch_pid (int);
+       void evma_unwatch_pid (const unsigned long);
+
+       void evma_start_proxy(const unsigned long, const unsigned long, const unsigned long);
+       void evma_stop_proxy(const unsigned long);
+
+       int evma_set_rlimit_nofile (int n_files);
+
+       void evma_set_epoll (int use);
+       void evma_set_kqueue (int use);
+
+#if __cplusplus
+}
+#endif
+
+
+#endif // __EventMachine__H_
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine_cpp.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/eventmachine_cpp.h
new file mode 100644 (file)
index 0000000..8c2d578
--- /dev/null
@@ -0,0 +1,96 @@
+/*****************************************************************************
+
+$Id$
+
+File:     eventmachine_cpp.h
+Date:     27Jul07
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+#ifndef __EVMA_EventmachineCpp__H_
+#define __EVMA_EventmachineCpp__H_
+
+
+// This material is only for directly integrating EM into C++ programs.
+
+namespace EM {
+
+       void Callback (const unsigned long sig, int event, const char *data, const unsigned long length);
+       void Run (void(*)(void));
+       void AddTimer (int, void(*)());
+       void StopReactor();
+
+       /***************
+       class Eventable
+       ***************/
+
+       class Eventable {
+               public:
+                       Eventable() {}
+                       virtual ~Eventable() {}
+
+                       unsigned long Signature;
+
+                       // Called by the framework
+                       virtual void ReceiveData (const char *data, int length) {}
+                       virtual void ConnectionCompleted() {}
+                       virtual void Accept (const unsigned long) {}
+                       virtual void Unbind() {}
+                       virtual void PostInit() {}
+                       virtual void SslHandshakeCompleted() {}
+
+                       void StopReactor() {EM::StopReactor();}
+       };
+
+       /****************
+       class Connection
+       ****************/
+
+       class Connection: public Eventable {
+               public:
+                       Connection() {}
+                       virtual ~Connection() {}
+
+                       virtual void Connect (const char*, int);
+                       virtual void BindConnect (const char *, int, const char*, int);
+
+                       void SendData (const char *data);
+                       void SendData (const char *data, int length);
+                       void Close (bool afterWriting);
+       };
+
+
+       /**************
+       class Acceptor
+       **************/
+
+       class Acceptor: public Eventable {
+               public:
+                       Acceptor() {PostInit();}
+                       virtual ~Acceptor() {}
+
+                       void Start (const char*, int);
+                       void Accept (const unsigned long);
+
+                       virtual Connection *MakeConnection() {return new Connection();}
+       };
+
+
+};
+
+
+
+
+
+#endif // __EVMA_EventmachineCpp__H_
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/extconf.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/extconf.rb
new file mode 100644 (file)
index 0000000..d6d6963
--- /dev/null
@@ -0,0 +1,148 @@
+require 'mkmf'
+
+def check_libs libs = [], fatal = false
+  libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
+end
+
+def check_heads heads = [], fatal = false
+  heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
+end
+
+def add_define(name)
+  $defs.push("-D#{name}")
+end
+
+add_define 'BUILD_FOR_RUBY'
+add_define 'HAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
+add_define "HAVE_TBR" if have_func('rb_thread_blocking_region')# and have_macro('RUBY_UBF_IO', 'ruby.h')
+add_define "HAVE_INOTIFY" if inotify = have_func('inotify_init', 'sys/inotify.h')
+add_define "HAVE_OLD_INOTIFY" if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
+add_define 'HAVE_WRITEV' if have_func('writev', 'sys/uio.h')
+have_func('rb_thread_check_ints')
+have_func('rb_time_new')
+
+# Minor platform details between *nix and Windows:
+
+if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
+  GNU_CHAIN = $1 == 'mingw'
+  OS_WIN32 = true
+  add_define "OS_WIN32"
+else
+  GNU_CHAIN = true
+  OS_UNIX = true
+  add_define 'OS_UNIX'
+
+  add_define "HAVE_KQUEUE" if have_header("sys/event.h") and have_header("sys/queue.h")
+end
+
+# Main platform invariances:
+
+case RUBY_PLATFORM
+when /mswin32/, /mingw32/, /bccwin32/
+  check_heads(%w[windows.h winsock.h], true)
+  check_libs(%w[kernel32 rpcrt4 gdi32], true)
+
+  if GNU_CHAIN
+    CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++"
+  else
+    $defs.push "-EHs"
+    $defs.push "-GR"
+  end
+
+when /solaris/
+  add_define 'OS_SOLARIS8'
+  check_libs(%w[nsl socket], true)
+
+  # Patch by Tim Pease, fixes SUNWspro compile problems.
+  if CONFIG['CC'] == 'cc'
+    # SUN CHAIN
+    $CFLAGS = CONFIG['CFLAGS'] = "-KPIC -G"
+    CONFIG['CCDLFLAGS'] = "-KPIC"
+  else
+    # GNU CHAIN
+    # on Unix we need a g++ link, not gcc.
+    CONFIG['LDSHARED'] = "$(CXX) -shared"
+  end
+
+when /openbsd/
+  # OpenBSD branch contributed by Guillaume Sellier.
+
+  # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
+  CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
+  CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
+
+when /darwin/
+  # on Unix we need a g++ link, not gcc.
+  # Ff line contributed by Daniel Harple.
+  CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
+
+when /linux/
+  add_define 'HAVE_EPOLL' if have_func('epoll_create', 'sys/epoll.h')
+
+  # Original epoll test is inadequate because 2.4 kernels have the header
+  # but not the code.
+  # add_define 'HAVE_EPOLL' if have_header('sys/epoll.h')
+  # if have_header('sys/epoll.h')
+  #   File.open("hasEpollTest.c", "w") {|f|
+  #     f.puts "#include <sys/epoll.h>"
+  #     f.puts "int main() { epoll_create(1024); return 0;}"
+  #   }
+  #   (e = system( "gcc hasEpollTest.c -o hasEpollTest " )) and (e = $?.to_i)
+  #   `rm -f hasEpollTest.c hasEpollTest`
+  #   add_define 'HAVE_EPOLL' if e == 0
+  # end
+
+  # on Unix we need a g++ link, not gcc.
+  CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+when /aix/
+  CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G -Wl,-brtl"
+
+else
+  # on Unix we need a g++ link, not gcc.
+  CONFIG['LDSHARED'] = "$(CXX) -shared"
+end
+
+# OpenSSL:
+
+def manual_ssl_config
+  ssl_libs_heads_args = {
+    :unix => [%w[ssl crypto], %w[openssl/ssl.h openssl/err.h]],
+    :darwin => [%w[ssl crypto C], %w[openssl/ssl.h openssl/err.h]],
+    # openbsd and linux:
+    :crypto_hack => [%w[crypto ssl crypto], %w[openssl/ssl.h openssl/err.h]],
+    :mswin => [%w[ssleay32 libeay32], %w[openssl/ssl.h openssl/err.h]],
+  }
+
+  dc_flags = ['ssl']
+  dc_flags += ["#{ENV['OPENSSL']}/include", ENV['OPENSSL']] if /linux/ =~ RUBY_PLATFORM
+
+  libs, heads = case RUBY_PLATFORM
+  when /mswin/    ; ssl_libs_heads_args[:mswin]
+  when /mingw/    ; ssl_libs_heads_args[:unix]
+  when /darwin/   ; ssl_libs_heads_args[:darwin]
+  when /openbsd/  ; ssl_libs_heads_args[:crypto_hack]
+  when /linux/    ; ssl_libs_heads_args[:crypto_hack]
+  else              ssl_libs_heads_args[:unix]
+  end
+  dir_config(*dc_flags)
+  check_libs(libs) and check_heads(heads)
+end
+
+# Try to use pkg_config first, fixes #73
+if pkg_config('openssl') || manual_ssl_config
+  add_define "WITH_SSL"
+else
+  add_define "WITHOUT_SSL"
+end
+
+# solaris c++ compiler doesn't have make_pair()
+TRY_LINK.sub!('$(CC)', '$(CXX)')
+add_define 'HAVE_MAKE_PAIR' if try_link(<<SRC, '-lstdc++')
+  #include <utility>
+  using namespace std;
+  int main(){ pair<int,int> tuple = make_pair(1,2); }
+SRC
+TRY_LINK.sub!('$(CXX)', '$(CC)')
+
+create_makefile "rubyeventmachine"
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/extconf.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/extconf.rb
new file mode 100644 (file)
index 0000000..4c55836
--- /dev/null
@@ -0,0 +1,83 @@
+require 'mkmf'
+
+def check_libs libs = [], fatal = false
+  libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
+end
+
+def check_heads heads = [], fatal = false
+  heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
+end
+
+def add_define(name)
+  $defs.push("-D#{name}")
+end
+
+add_define 'BUILD_FOR_RUBY'
+
+# Minor platform details between *nix and Windows:
+
+if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
+  GNU_CHAIN = $1 == 'mingw'
+  OS_WIN32 = true
+  add_define "OS_WIN32"
+else
+  GNU_CHAIN = true
+  OS_UNIX = true
+  add_define 'OS_UNIX'
+end
+
+# Main platform invariances:
+
+case RUBY_PLATFORM
+when /mswin32/, /mingw32/, /bccwin32/
+  check_heads(%w[windows.h winsock.h], true)
+  check_libs(%w[kernel32 rpcrt4 gdi32], true)
+
+  if GNU_CHAIN
+    CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++"
+  else
+    $defs.push "-EHs"
+    $defs.push "-GR"
+  end
+
+when /solaris/
+  add_define 'OS_SOLARIS8'
+  check_libs(%w[nsl socket], true)
+
+  # Patch by Tim Pease, fixes SUNWspro compile problems.
+  if CONFIG['CC'] == 'cc'
+    # SUN CHAIN
+    $CFLAGS = CONFIG['CFLAGS'] = "-KPIC -G"
+    CONFIG['CCDLFLAGS'] = "-KPIC"
+  else
+    # GNU CHAIN
+    # on Unix we need a g++ link, not gcc.
+    CONFIG['LDSHARED'] = "$(CXX) -shared"
+  end
+
+when /openbsd/
+  # OpenBSD branch contributed by Guillaume Sellier.
+
+  # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
+  CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
+  CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
+
+when /darwin/
+  # on Unix we need a g++ link, not gcc.
+  # Ff line contributed by Daniel Harple.
+  CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
+
+when /linux/
+  # on Unix we need a g++ link, not gcc.
+  CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+when /aix/
+  # on Unix we need a g++ link, not gcc.
+  CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G"
+
+else
+  # on Unix we need a g++ link, not gcc.
+  CONFIG['LDSHARED'] = "$(CXX) -shared"
+end
+
+create_makefile "fastfilereaderext"
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.cpp
new file mode 100644 (file)
index 0000000..77e95af
--- /dev/null
@@ -0,0 +1,214 @@
+/*****************************************************************************\r
+\r
+$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $\r
+\r
+File:     mapper.cpp\r
+Date:     02Jul07\r
+\r
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: garbagecat10\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// UNIX implementation\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+\r
+#ifdef OS_UNIX\r
+\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <sys/mman.h>\r
+#include <fcntl.h>\r
+#include <errno.h>\r
+\r
+#include <iostream>\r
+#include "unistd.h"\r
+#include <string>\r
+#include <cstring>\r
+#include <stdexcept>\r
+using namespace std;\r
+\r
+#include "mapper.h"\r
+\r
+/******************\r
+Mapper_t::Mapper_t\r
+******************/\r
+\r
+Mapper_t::Mapper_t (const string &filename)\r
+{\r
+       /* We ASSUME we can open the file.\r
+        * (More precisely, we assume someone else checked before we got here.)\r
+        */\r
+\r
+       Fd = open (filename.c_str(), O_RDONLY);\r
+       if (Fd < 0)\r
+               throw runtime_error (strerror (errno));\r
+\r
+       struct stat st;\r
+       if (fstat (Fd, &st))\r
+               throw runtime_error (strerror (errno));\r
+       FileSize = st.st_size;\r
+\r
+       #ifdef OS_WIN32\r
+       MapPoint = (char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);\r
+       #else\r
+       MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);\r
+       #endif\r
+       if (MapPoint == MAP_FAILED)\r
+               throw runtime_error (strerror (errno));\r
+}\r
+\r
+\r
+/*******************\r
+Mapper_t::~Mapper_t\r
+*******************/\r
+\r
+Mapper_t::~Mapper_t()\r
+{\r
+       Close();\r
+}\r
+\r
+\r
+/***************\r
+Mapper_t::Close\r
+***************/\r
+\r
+void Mapper_t::Close()\r
+{\r
+       // Can be called multiple times.\r
+       // Calls to GetChunk are invalid after a call to Close.\r
+       if (MapPoint) {\r
+               #ifdef OS_SOLARIS8\r
+               munmap ((char*)MapPoint, FileSize);\r
+               #else\r
+               munmap ((void*)MapPoint, FileSize);\r
+               #endif\r
+               MapPoint = NULL;\r
+       }\r
+       if (Fd >= 0) {\r
+               close (Fd);\r
+               Fd = -1;\r
+       }\r
+}\r
+\r
+/******************\r
+Mapper_t::GetChunk\r
+******************/\r
+\r
+const char *Mapper_t::GetChunk (unsigned start)\r
+{\r
+       return MapPoint + start;\r
+}\r
+\r
+\r
+\r
+#endif // OS_UNIX\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// WINDOWS implementation\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef OS_WIN32\r
+\r
+#include <windows.h>\r
+\r
+#include <iostream>\r
+#include <string>\r
+#include <stdexcept>\r
+using namespace std;\r
+\r
+#include "mapper.h"\r
+\r
+/******************\r
+Mapper_t::Mapper_t\r
+******************/\r
+\r
+Mapper_t::Mapper_t (const string &filename)\r
+{\r
+       /* We ASSUME we can open the file.\r
+        * (More precisely, we assume someone else checked before we got here.)\r
+        */\r
+\r
+       hFile = INVALID_HANDLE_VALUE;\r
+       hMapping = NULL;\r
+       MapPoint = NULL;\r
+       FileSize = 0;\r
+\r
+       hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
+\r
+       if (hFile == INVALID_HANDLE_VALUE)\r
+               throw runtime_error ("File not found");\r
+\r
+       BY_HANDLE_FILE_INFORMATION i;\r
+       if (GetFileInformationByHandle (hFile, &i))\r
+               FileSize = i.nFileSizeLow;\r
+\r
+       hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);\r
+       if (!hMapping)\r
+               throw runtime_error ("File not mapped");\r
+\r
+       #ifdef OS_WIN32\r
+       MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);\r
+       #else\r
+       MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);\r
+       #endif\r
+       if (!MapPoint)\r
+               throw runtime_error ("Mappoint not read");\r
+}\r
+\r
+\r
+/*******************\r
+Mapper_t::~Mapper_t\r
+*******************/\r
+\r
+Mapper_t::~Mapper_t()\r
+{\r
+       Close();\r
+}\r
+\r
+/***************\r
+Mapper_t::Close\r
+***************/\r
+\r
+void Mapper_t::Close()\r
+{\r
+       // Can be called multiple times.\r
+       // Calls to GetChunk are invalid after a call to Close.\r
+       if (MapPoint) {\r
+               UnmapViewOfFile (MapPoint);\r
+               MapPoint = NULL;\r
+       }\r
+       if (hMapping != NULL) {\r
+               CloseHandle (hMapping);\r
+               hMapping = NULL;\r
+       }\r
+       if (hFile != INVALID_HANDLE_VALUE) {\r
+               CloseHandle (hFile);\r
+               hMapping = INVALID_HANDLE_VALUE;\r
+       }\r
+}\r
+\r
+\r
+/******************\r
+Mapper_t::GetChunk\r
+******************/\r
+\r
+const char *Mapper_t::GetChunk (unsigned start)\r
+{\r
+       return MapPoint + start;\r
+}\r
+\r
+\r
+\r
+#endif // OS_WINDOWS\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/mapper.h
new file mode 100644 (file)
index 0000000..efbc104
--- /dev/null
@@ -0,0 +1,59 @@
+/*****************************************************************************\r
+\r
+$Id: mapper.h 4529 2007-07-04 11:32:22Z francis $\r
+\r
+File:     mapper.h\r
+Date:     02Jul07\r
+\r
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: garbagecat10\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifndef __Mapper__H_\r
+#define __Mapper__H_\r
+\r
+\r
+/**************\r
+class Mapper_t\r
+**************/\r
+\r
+class Mapper_t\r
+{\r
+       public:\r
+               Mapper_t (const string&);\r
+               virtual ~Mapper_t();\r
+\r
+               const char *GetChunk (unsigned);\r
+               void Close();\r
+               size_t GetFileSize() {return FileSize;}\r
+\r
+       private:\r
+               size_t FileSize;\r
+\r
+       #ifdef OS_UNIX\r
+       private:\r
+               int Fd;\r
+               const char *MapPoint;\r
+       #endif // OS_UNIX\r
+\r
+       #ifdef OS_WIN32\r
+       private:\r
+               HANDLE hFile;\r
+               HANDLE hMapping;\r
+               char *MapPoint;\r
+       #endif // OS_WIN32\r
+\r
+};\r
+\r
+\r
+#endif // __Mapper__H_\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/rubymain.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/fastfilereader/rubymain.cpp
new file mode 100644 (file)
index 0000000..2ab8c2a
--- /dev/null
@@ -0,0 +1,127 @@
+/*****************************************************************************\r
+\r
+$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $\r
+\r
+File:     rubymain.cpp\r
+Date:     02Jul07\r
+\r
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: garbagecat10\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+\r
+#include <iostream>\r
+#include <stdexcept>\r
+using namespace std;\r
+\r
+#include <ruby.h>\r
+#include "mapper.h"\r
+\r
+static VALUE EmModule;\r
+static VALUE FastFileReader;\r
+static VALUE Mapper;\r
+\r
+\r
+\r
+/*********\r
+mapper_dt\r
+*********/\r
+\r
+static void mapper_dt (void *ptr)\r
+{\r
+       if (ptr)\r
+               delete (Mapper_t*) ptr;\r
+}\r
+\r
+/**********\r
+mapper_new\r
+**********/\r
+\r
+static VALUE mapper_new (VALUE self, VALUE filename)\r
+{\r
+       Mapper_t *m = new Mapper_t (StringValuePtr (filename));\r
+       if (!m)\r
+               rb_raise (rb_eException, "No Mapper Object");\r
+       VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);\r
+       return v;\r
+}\r
+\r
+\r
+/****************\r
+mapper_get_chunk\r
+****************/\r
+\r
+static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)\r
+{\r
+       Mapper_t *m = NULL;\r
+       Data_Get_Struct (self, Mapper_t, m);\r
+       if (!m)\r
+               rb_raise (rb_eException, "No Mapper Object");\r
+\r
+       // TODO, what if some moron sends us a negative start value?\r
+       unsigned _start = NUM2INT (start);\r
+       unsigned _length = NUM2INT (length);\r
+       if ((_start + _length) > m->GetFileSize())\r
+               rb_raise (rb_eException, "Mapper Range Error");\r
+\r
+       const char *chunk = m->GetChunk (_start);\r
+       if (!chunk)\r
+               rb_raise (rb_eException, "No Mapper Chunk");\r
+       return rb_str_new (chunk, _length);\r
+}\r
+\r
+/************\r
+mapper_close\r
+************/\r
+\r
+static VALUE mapper_close (VALUE self)\r
+{\r
+       Mapper_t *m = NULL;\r
+       Data_Get_Struct (self, Mapper_t, m);\r
+       if (!m)\r
+               rb_raise (rb_eException, "No Mapper Object");\r
+       m->Close();\r
+       return Qnil;\r
+}\r
+\r
+/***********\r
+mapper_size\r
+***********/\r
+\r
+static VALUE mapper_size (VALUE self)\r
+{\r
+       Mapper_t *m = NULL;\r
+       Data_Get_Struct (self, Mapper_t, m);\r
+       if (!m)\r
+               rb_raise (rb_eException, "No Mapper Object");\r
+       return INT2NUM (m->GetFileSize());\r
+}\r
+\r
+\r
+/**********************\r
+Init_fastfilereaderext\r
+**********************/\r
+\r
+extern "C" void Init_fastfilereaderext()\r
+{\r
+       EmModule = rb_define_module ("EventMachine");\r
+       FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject);\r
+       Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject);\r
+\r
+       rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1);\r
+       rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0);\r
+       rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0);\r
+       rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2);\r
+}\r
+\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.cpp
new file mode 100644 (file)
index 0000000..3f09331
--- /dev/null
@@ -0,0 +1,94 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     files.cpp\r
+Date:     26Aug06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+#include "project.h"\r
+\r
+\r
+/******************************************\r
+FileStreamDescriptor::FileStreamDescriptor\r
+******************************************/\r
+\r
+FileStreamDescriptor::FileStreamDescriptor (int fd, EventMachine_t *em):\r
+       EventableDescriptor (fd, em),\r
+       OutboundDataSize (0)\r
+{\r
+cerr << "#####";\r
+}\r
+\r
+\r
+/*******************************************\r
+FileStreamDescriptor::~FileStreamDescriptor\r
+*******************************************/\r
+\r
+FileStreamDescriptor::~FileStreamDescriptor()\r
+{\r
+       // Run down any stranded outbound data.\r
+       for (size_t i=0; i < OutboundPages.size(); i++)\r
+               OutboundPages[i].Free();\r
+}\r
+\r
+\r
+/**************************\r
+FileStreamDescriptor::Read\r
+**************************/\r
+\r
+void FileStreamDescriptor::Read()\r
+{\r
+}\r
+\r
+/***************************\r
+FileStreamDescriptor::Write\r
+***************************/\r
+\r
+void FileStreamDescriptor::Write()\r
+{\r
+}\r
+\r
+\r
+/*******************************\r
+FileStreamDescriptor::Heartbeat\r
+*******************************/\r
+\r
+void FileStreamDescriptor::Heartbeat()\r
+{\r
+}\r
+\r
+\r
+/***********************************\r
+FileStreamDescriptor::SelectForRead\r
+***********************************/\r
+\r
+bool FileStreamDescriptor::SelectForRead()\r
+{\r
+  cerr << "R?";\r
+  return false;\r
+}\r
+\r
+\r
+/************************************\r
+FileStreamDescriptor::SelectForWrite\r
+************************************/\r
+\r
+bool FileStreamDescriptor::SelectForWrite()\r
+{\r
+  cerr << "W?";\r
+  return false;\r
+}\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/files.h
new file mode 100644 (file)
index 0000000..c9d2092
--- /dev/null
@@ -0,0 +1,65 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     files.h\r
+Date:     26Aug06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifndef __FileStreamDescriptor__H_\r
+#define __FileStreamDescriptor__H_\r
+\r
+\r
+\r
+/**************************\r
+class FileStreamDescriptor\r
+**************************/\r
+\r
+class FileStreamDescriptor: public EventableDescriptor\r
+{\r
+       public:\r
+               FileStreamDescriptor (int, EventMachine_t*);\r
+               virtual ~FileStreamDescriptor();\r
+\r
+               virtual void Read();\r
+               virtual void Write();\r
+               virtual void Heartbeat();\r
+\r
+               virtual bool SelectForRead();\r
+               virtual bool SelectForWrite();\r
+\r
+               // Do we have any data to write? This is used by ShouldDelete.\r
+               virtual int GetOutboundDataSize() {return OutboundDataSize;}\r
+\r
+       protected:\r
+               struct OutboundPage {\r
+                       OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}\r
+                       void Free() {if (Buffer) free ((char*)Buffer); }\r
+                       const char *Buffer;\r
+                       int Length;\r
+                       int Offset;\r
+               };\r
+\r
+       protected:\r
+               deque<OutboundPage> OutboundPages;\r
+               int OutboundDataSize;\r
+\r
+       private:\r
+\r
+};\r
+\r
+\r
+#endif // __FileStreamDescriptor__H_\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/kb.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/kb.cpp
new file mode 100644 (file)
index 0000000..802f550
--- /dev/null
@@ -0,0 +1,81 @@
+/*****************************************************************************
+
+$Id$
+
+File:     kb.cpp
+Date:     24Aug07
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+/**************************************
+KeyboardDescriptor::KeyboardDescriptor
+**************************************/
+
+KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
+       EventableDescriptor (0, parent_em),
+       bReadAttemptedAfterClose (false),
+       LastIo (gCurrentLoopTime),
+       InactivityTimeout (0)
+{
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueReader (this);
+       #endif
+}
+
+
+/***************************************
+KeyboardDescriptor::~KeyboardDescriptor
+***************************************/
+
+KeyboardDescriptor::~KeyboardDescriptor()
+{
+}
+
+
+/*************************
+KeyboardDescriptor::Write
+*************************/
+
+void KeyboardDescriptor::Write()
+{
+       // Why are we here?
+       throw std::runtime_error ("bad code path in keyboard handler");
+}
+
+
+/*****************************
+KeyboardDescriptor::Heartbeat
+*****************************/
+
+void KeyboardDescriptor::Heartbeat()
+{
+       // no-op
+}
+
+
+/************************
+KeyboardDescriptor::Read
+************************/
+
+void KeyboardDescriptor::Read()
+{
+       char c;
+       read (GetSocket(), &c, 1);
+       _GenericInboundDispatch(&c, 1);
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.cpp
new file mode 100644 (file)
index 0000000..8429ff2
--- /dev/null
@@ -0,0 +1,107 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     page.cpp\r
+Date:     30Apr06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#include "project.h"\r
+\r
+\r
+/******************\r
+PageList::PageList\r
+******************/\r
+\r
+PageList::PageList()\r
+{\r
+}\r
+\r
+\r
+/*******************\r
+PageList::~PageList\r
+*******************/\r
+\r
+PageList::~PageList()\r
+{\r
+       while (HasPages())\r
+               PopFront();\r
+}\r
+\r
+\r
+/***************\r
+PageList::Front\r
+***************/\r
+\r
+void PageList::Front (const char **page, int *length)\r
+{\r
+       assert (page && length);\r
+\r
+       if (HasPages()) {\r
+               Page p = Pages.front();\r
+               *page = p.Buffer;\r
+               *length = p.Size;\r
+       }\r
+       else {\r
+               *page = NULL;\r
+               *length = 0;\r
+       }\r
+}\r
+\r
+\r
+/******************\r
+PageList::PopFront\r
+******************/\r
+\r
+void PageList::PopFront()\r
+{\r
+       if (HasPages()) {\r
+               Page p = Pages.front();\r
+               Pages.pop_front();\r
+               if (p.Buffer)\r
+                       free ((void*)p.Buffer);\r
+       }\r
+}\r
+\r
+\r
+/******************\r
+PageList::HasPages\r
+******************/\r
+\r
+bool PageList::HasPages()\r
+{\r
+       return (Pages.size() > 0) ? true : false;\r
+}\r
+\r
+\r
+/**************\r
+PageList::Push\r
+**************/\r
+\r
+void PageList::Push (const char *buf, int size)\r
+{\r
+       if (buf && (size > 0)) {\r
+               char *copy = (char*) malloc (size);\r
+               if (!copy)\r
+                       throw runtime_error ("no memory in pagelist");\r
+               memcpy (copy, buf, size);\r
+               Pages.push_back (Page (copy, size));\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/page.h
new file mode 100644 (file)
index 0000000..36009ab
--- /dev/null
@@ -0,0 +1,51 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     page.h\r
+Date:     30Apr06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifndef __PageManager__H_\r
+#define __PageManager__H_\r
+\r
+\r
+/**************\r
+class PageList\r
+**************/\r
+\r
+class PageList\r
+{\r
+       struct Page {\r
+               Page (const char *b, size_t s): Buffer(b), Size(s) {}\r
+               const char *Buffer;\r
+               size_t Size;\r
+       };\r
+\r
+       public:\r
+               PageList();\r
+               virtual ~PageList();\r
+\r
+               void Push (const char*, int);\r
+               bool HasPages();\r
+               void Front (const char**, int*);\r
+               void PopFront();\r
+\r
+       private:\r
+               deque<Page> Pages;\r
+};\r
+\r
+\r
+#endif // __PageManager__H_\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/pipe.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/pipe.cpp
new file mode 100644 (file)
index 0000000..1f29783
--- /dev/null
@@ -0,0 +1,349 @@
+/*****************************************************************************
+
+$Id$
+
+File:     pipe.cpp
+Date:     30May07
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+#ifdef OS_UNIX
+// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
+
+/******************************
+PipeDescriptor::PipeDescriptor
+******************************/
+
+PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
+       EventableDescriptor (fd, parent_em),
+       bReadAttemptedAfterClose (false),
+       LastIo (gCurrentLoopTime),
+       InactivityTimeout (0),
+       OutboundDataSize (0),
+       SubprocessPid (subpid)
+{
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = EPOLLIN;
+       #endif
+       #ifdef HAVE_KQUEUE
+       MyEventMachine->ArmKqueueReader (this);
+       #endif
+}
+
+
+/*******************************
+PipeDescriptor::~PipeDescriptor
+*******************************/
+
+PipeDescriptor::~PipeDescriptor()
+{
+       // Run down any stranded outbound data.
+       for (size_t i=0; i < OutboundPages.size(); i++)
+               OutboundPages[i].Free();
+
+       /* As a virtual destructor, we come here before the base-class
+        * destructor that closes our file-descriptor.
+        * We have to make sure the subprocess goes down (if it's not
+        * already down) and we have to reap the zombie.
+        *
+        * This implementation is PROVISIONAL and will surely be improved.
+        * The intention here is that we never block, hence the highly
+        * undesirable sleeps. But if we can't reap the subprocess even
+        * after sending it SIGKILL, then something is wrong and we
+        * throw a fatal exception, which is also not something we should
+        * be doing.
+        *
+        * Eventually the right thing to do will be to have the reactor
+        * core respond to SIGCHLD by chaining a handler on top of the
+        * one Ruby may have installed, and dealing with a list of dead
+        * children that are pending cleanup.
+        *
+        * Since we want to have a signal processor integrated into the
+        * client-visible API, let's wait until that is done before cleaning
+        * this up.
+        *
+        * Added a very ugly hack to support passing the subprocess's exit
+        * status to the user. It only makes logical sense for user code to access
+        * the subprocess exit status in the unbind callback. But unbind is called
+        * back during the EventableDescriptor destructor. So by that time there's
+        * no way to call back this object through an object binding, because it's
+        * already been cleaned up. We might have added a parameter to the unbind
+        * callback, but that would probably break a huge amount of existing code.
+        * So the hack-solution is to define an instance variable in the EventMachine
+        * object and stick the exit status in there, where it can easily be accessed
+        * with an accessor visible to user code.
+        * User code should ONLY access the exit status from within the unbind callback.
+        * Otherwise there's no guarantee it'll be valid.
+        * This hack won't make it impossible to run multiple EventMachines in a single
+        * process, but it will make it impossible to reliably nest unbind calls
+        * within other unbind calls. (Not sure if that's even possible.)
+        */
+
+       assert (MyEventMachine);
+
+       /* Another hack to make the SubprocessPid available to get_subprocess_status */
+       MyEventMachine->SubprocessPid = SubprocessPid;
+
+       /* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
+        * it resumes the system call after processing the signal (resulting in unnecessary latency).
+        * Calling nanosleep in a loop avoids this problem.
+        */
+       struct timespec req = {0, 50000000}; // 0.05s
+       int n;
+
+       // wait 0.25s for the process to die
+       for (n=0; n<5; n++) {
+               if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+               nanosleep (&req, NULL);
+       }
+
+       // send SIGTERM and wait another 0.5s
+       kill (SubprocessPid, SIGTERM);
+       for (n=0; n<10; n++) {
+               nanosleep (&req, NULL);
+               if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+       }
+
+       // send SIGKILL and wait another 1s
+       kill (SubprocessPid, SIGKILL);
+       for (n=0; n<20; n++) {
+               nanosleep (&req, NULL);
+               if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+       }
+
+       // still not dead, give up!
+       throw std::runtime_error ("unable to reap subprocess");
+}
+
+
+
+/********************
+PipeDescriptor::Read
+********************/
+
+void PipeDescriptor::Read()
+{
+       int sd = GetSocket();
+       if (sd == INVALID_SOCKET) {
+               assert (!bReadAttemptedAfterClose);
+               bReadAttemptedAfterClose = true;
+               return;
+       }
+
+       LastIo = gCurrentLoopTime;
+
+       int total_bytes_read = 0;
+       char readbuffer [16 * 1024];
+
+       for (int i=0; i < 10; i++) {
+               // Don't read just one buffer and then move on. This is faster
+               // if there is a lot of incoming.
+               // But don't read indefinitely. Give other sockets a chance to run.
+               // NOTICE, we're reading one less than the buffer size.
+               // That's so we can put a guard byte at the end of what we send
+               // to user code.
+               // Use read instead of recv, which on Linux gives a "socket operation
+               // on nonsocket" error.
+               
+
+               int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
+               //cerr << "<R:" << r << ">";
+
+               if (r > 0) {
+                       total_bytes_read += r;
+
+                       // Add a null-terminator at the the end of the buffer
+                       // that we will send to the callback.
+                       // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+                       // to be able to depend on this behavior, so they will have
+                       // the option to do some things faster. Additionally it's
+                       // a security guard against buffer overflows.
+                       readbuffer [r] = 0;
+                       _GenericInboundDispatch(readbuffer, r);
+                       }
+               else if (r == 0) {
+                       break;
+               }
+               else {
+                       // Basically a would-block, meaning we've read everything there is to read.
+                       break;
+               }
+
+       }
+
+
+       if (total_bytes_read == 0) {
+               // If we read no data on a socket that selected readable,
+               // it generally means the other end closed the connection gracefully.
+               ScheduleClose (false);
+               //bCloseNow = true;
+       }
+
+}
+
+/*********************
+PipeDescriptor::Write
+*********************/
+
+void PipeDescriptor::Write()
+{
+       int sd = GetSocket();
+       assert (sd != INVALID_SOCKET);
+
+       LastIo = gCurrentLoopTime;
+       char output_buffer [16 * 1024];
+       size_t nbytes = 0;
+
+       while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
+               OutboundPage *op = &(OutboundPages[0]);
+               if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
+                       memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
+                       nbytes += (op->Length - op->Offset);
+                       op->Free();
+                       OutboundPages.pop_front();
+               }
+               else {
+                       int len = sizeof(output_buffer) - nbytes;
+                       memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
+                       op->Offset += len;
+                       nbytes += len;
+               }
+       }
+
+       // We should never have gotten here if there were no data to write,
+       // so assert that as a sanity check.
+       // Don't bother to make sure nbytes is less than output_buffer because
+       // if it were we probably would have crashed already.
+       assert (nbytes > 0);
+
+       assert (GetSocket() != INVALID_SOCKET);
+       int bytes_written = write (GetSocket(), output_buffer, nbytes);
+
+       if (bytes_written > 0) {
+               OutboundDataSize -= bytes_written;
+               if ((size_t)bytes_written < nbytes) {
+                       int len = nbytes - bytes_written;
+                       char *buffer = (char*) malloc (len + 1);
+                       if (!buffer)
+                               throw std::runtime_error ("bad alloc throwing back data");
+                       memcpy (buffer, output_buffer + bytes_written, len);
+                       buffer [len] = 0;
+                       OutboundPages.push_front (OutboundPage (buffer, len));
+               }
+               #ifdef HAVE_EPOLL
+               EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
+               assert (MyEventMachine);
+               MyEventMachine->Modify (this);
+               #endif
+       }
+       else {
+               #ifdef OS_UNIX
+               if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
+               #endif
+               #ifdef OS_WIN32
+               if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
+               #endif
+                       Close();
+       }
+}
+
+
+/*************************
+PipeDescriptor::Heartbeat
+*************************/
+
+void PipeDescriptor::Heartbeat()
+{
+       // If an inactivity timeout is defined, then check for it.
+       if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
+               ScheduleClose (false);
+               //bCloseNow = true;
+}
+
+
+/*****************************
+PipeDescriptor::SelectForRead
+*****************************/
+
+bool PipeDescriptor::SelectForRead()
+{
+       /* Pipe descriptors, being local by definition, don't have
+        * a pending state, so this is simpler than for the
+        * ConnectionDescriptor object.
+        */
+       return true;
+}
+
+/******************************
+PipeDescriptor::SelectForWrite
+******************************/
+
+bool PipeDescriptor::SelectForWrite()
+{
+       /* Pipe descriptors, being local by definition, don't have
+        * a pending state, so this is simpler than for the
+        * ConnectionDescriptor object.
+        */
+       return (GetOutboundDataSize() > 0);
+}
+
+
+
+
+/********************************
+PipeDescriptor::SendOutboundData
+********************************/
+
+int PipeDescriptor::SendOutboundData (const char *data, int length)
+{
+       //if (bCloseNow || bCloseAfterWriting)
+       if (IsCloseScheduled())
+               return 0;
+
+       if (!data && (length > 0))
+               throw std::runtime_error ("bad outbound data");
+       char *buffer = (char *) malloc (length + 1);
+       if (!buffer)
+               throw std::runtime_error ("no allocation for outbound data");
+       memcpy (buffer, data, length);
+       buffer [length] = 0;
+       OutboundPages.push_back (OutboundPage (buffer, length));
+       OutboundDataSize += length;
+       #ifdef HAVE_EPOLL
+       EpollEvent.events = (EPOLLIN | EPOLLOUT);
+       assert (MyEventMachine);
+       MyEventMachine->Modify (this);
+       #endif
+       return length;
+}
+
+/********************************
+PipeDescriptor::GetSubprocessPid
+********************************/
+
+bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
+{
+       bool ok = false;
+       if (pid && (SubprocessPid > 0)) {
+               *pid = SubprocessPid;
+               ok = true;
+       }
+       return ok;
+}
+
+
+#endif // OS_UNIX
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/project.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/project.h
new file mode 100644 (file)
index 0000000..ad296f8
--- /dev/null
@@ -0,0 +1,151 @@
+/*****************************************************************************
+
+$Id$
+
+File:     project.h
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+#ifndef __Project__H_
+#define __Project__H_
+
+
+#ifdef OS_WIN32
+#pragma warning(disable:4786)
+#endif
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <vector>
+#include <deque>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+
+#ifdef OS_UNIX
+#include <signal.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+typedef int SOCKET;
+#define closesocket close
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#ifdef OS_SOLARIS8
+#include <strings.h>
+#include <sys/un.h>
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+// INADDR_NONE is undefined on Solaris < 8. Thanks to Brett Eisenberg and Tim Pease.
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long)-1)
+#endif
+#endif /* OS_SOLARIS8 */
+
+#ifdef _AIX
+#include <strings.h>
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+#endif /* _AIX */
+
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+// 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h)
+#define FD_SETSIZE 1024
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <rpc.h>
+#include <fcntl.h>
+#include <assert.h>
+
+typedef int socklen_t;
+typedef int pid_t;
+#endif
+
+
+using namespace std;
+
+#ifdef WITH_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#endif
+
+#ifdef HAVE_KQUEUE
+#include <sys/event.h>
+#include <sys/queue.h>
+#endif
+
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
+
+#ifdef HAVE_OLD_INOTIFY
+#include <sys/syscall.h>
+#include <linux/inotify.h>
+static inline int inotify_init (void) { return syscall (__NR_inotify_init); }
+static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); }
+static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); }
+#define HAVE_INOTIFY 1
+#endif
+
+#ifdef HAVE_INOTIFY
+#define INOTIFY_EVENT_SIZE  (sizeof(struct inotify_event))
+#endif
+
+#ifdef HAVE_WRITEV
+#include <sys/uio.h>
+#endif
+
+#include "binder.h"
+#include "em.h"
+#include "epoll.h"
+#include "sigs.h"
+#include "ed.h"
+#include "files.h"
+#include "page.h"
+#include "ssl.h"
+#include "eventmachine.h"
+#include "eventmachine_cpp.h"
+
+
+
+
+#endif // __Project__H_
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/rubymain.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/rubymain.cpp
new file mode 100644 (file)
index 0000000..1c2ff6a
--- /dev/null
@@ -0,0 +1,1166 @@
+/*****************************************************************************
+
+$Id$
+
+File:     rubymain.cpp
+Date:     06Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+#include "project.h"
+#include "eventmachine.h"
+#include <ruby.h>
+
+#ifndef RFLOAT_VALUE
+#define RFLOAT_VALUE(arg) RFLOAT(arg)->value
+#endif
+
+/*******
+Statics
+*******/
+
+static VALUE EmModule;
+static VALUE EmConnection;
+
+static VALUE EM_eConnectionError;
+static VALUE EM_eUnknownTimerFired;
+static VALUE EM_eConnectionNotBound;
+static VALUE EM_eUnsupported;
+
+static VALUE Intern_at_signature;
+static VALUE Intern_at_timers;
+static VALUE Intern_at_conns;
+static VALUE Intern_at_error_handler;
+static VALUE Intern_event_callback;
+static VALUE Intern_run_deferred_callbacks;
+static VALUE Intern_delete;
+static VALUE Intern_call;
+static VALUE Intern_receive_data;
+static VALUE Intern_ssl_handshake_completed;
+static VALUE Intern_ssl_verify_peer;
+static VALUE Intern_notify_readable;
+static VALUE Intern_notify_writable;
+static VALUE Intern_proxy_target_unbound;
+
+static VALUE rb_cProcStatus;
+
+struct em_event {
+       unsigned long a1;
+       int a2;
+       const char *a3;
+       unsigned long a4;
+};
+
+/****************
+t_event_callback
+****************/
+
+static void event_callback (struct em_event* e)
+{
+       const unsigned long a1 = e->a1;
+       int a2 = e->a2;
+       const char *a3 = e->a3;
+       const unsigned long a4 = e->a4;
+
+       if (a2 == EM_CONNECTION_READ) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %lu", a4, a1);
+               rb_funcall (q, Intern_receive_data, 1, rb_str_new (a3, a4));
+       }
+       else if (a2 == EM_CONNECTION_NOTIFY_READABLE) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", a1);
+               rb_funcall (q, Intern_notify_readable, 0);
+       }
+       else if (a2 == EM_CONNECTION_NOTIFY_WRITABLE) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", a1);
+               rb_funcall (q, Intern_notify_writable, 0);
+       }
+       else if (a2 == EM_LOOPBREAK_SIGNAL) {
+               rb_funcall (EmModule, Intern_run_deferred_callbacks, 0);
+       }
+       else if (a2 == EM_TIMER_FIRED) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_timers);
+               VALUE q = rb_funcall (t, Intern_delete, 1, ULONG2NUM (a4));
+               if (q == Qnil) {
+                       rb_raise (EM_eUnknownTimerFired, "no such timer: %lu", a4);
+               } else if (q == Qfalse) {
+                       /* Timer Canceled */
+               } else {
+                       rb_funcall (q, Intern_call, 0);
+               }
+       }
+       #ifdef WITH_SSL
+       else if (a2 == EM_SSL_HANDSHAKE_COMPLETED) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", a1);
+               rb_funcall (q, Intern_ssl_handshake_completed, 0);
+       }
+       else if (a2 == EM_SSL_VERIFY) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", a1);
+               VALUE r = rb_funcall (q, Intern_ssl_verify_peer, 1, rb_str_new(a3, a4));
+               if (RTEST(r))
+                       evma_accept_ssl_peer (a1);
+       }
+       #endif
+       else if (a2 == EM_PROXY_TARGET_UNBOUND) {
+               VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
+               VALUE q = rb_hash_aref (t, ULONG2NUM (a1));
+               if (q == Qnil)
+                       rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", a1);
+               rb_funcall (q, Intern_proxy_target_unbound, 0);
+       }
+       else
+               rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(a1), INT2FIX(a2), a3 ? rb_str_new(a3,a4) : ULONG2NUM(a4));
+}
+
+/*******************
+event_error_handler
+*******************/
+
+static void event_error_handler(VALUE unused, VALUE err)
+{
+       VALUE error_handler = rb_ivar_get(EmModule, Intern_at_error_handler);
+       rb_funcall (error_handler, Intern_call, 1, err);
+}
+
+/**********************
+event_callback_wrapper
+**********************/
+
+static void event_callback_wrapper (const unsigned long a1, int a2, const char *a3, const unsigned long a4)
+{
+       struct em_event e;
+       e.a1 = a1;
+       e.a2 = a2;
+       e.a3 = a3;
+       e.a4 = a4;
+
+       if (!rb_ivar_defined(EmModule, Intern_at_error_handler))
+               event_callback(&e);
+       else
+               rb_rescue((VALUE (*)(ANYARGS))event_callback, (VALUE)&e, (VALUE (*)(ANYARGS))event_error_handler, Qnil);
+}
+
+/**************************
+t_initialize_event_machine
+**************************/
+
+static VALUE t_initialize_event_machine (VALUE self)
+{
+       evma_initialize_library (event_callback_wrapper);
+       return Qnil;
+}
+
+
+
+/*****************************
+t_run_machine_without_threads
+*****************************/
+
+static VALUE t_run_machine_without_threads (VALUE self)
+{
+       evma_run_machine();
+       return Qnil;
+}
+
+
+/*******************
+t_add_oneshot_timer
+*******************/
+
+static VALUE t_add_oneshot_timer (VALUE self, VALUE interval)
+{
+       const unsigned long f = evma_install_oneshot_timer (FIX2INT (interval));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "ran out of timers; use #set_max_timers to increase limit");
+       return ULONG2NUM (f);
+}
+
+
+/**************
+t_start_server
+**************/
+
+static VALUE t_start_server (VALUE self, VALUE server, VALUE port)
+{
+       const unsigned long f = evma_create_tcp_server (StringValuePtr(server), FIX2INT(port));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no acceptor");
+       return ULONG2NUM (f);
+}
+
+/*************
+t_stop_server
+*************/
+
+static VALUE t_stop_server (VALUE self, VALUE signature)
+{
+       evma_stop_tcp_server (NUM2ULONG (signature));
+       return Qnil;
+}
+
+
+/*******************
+t_start_unix_server
+*******************/
+
+static VALUE t_start_unix_server (VALUE self, VALUE filename)
+{
+       const unsigned long f = evma_create_unix_domain_server (StringValuePtr(filename));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no unix-domain acceptor");
+       return ULONG2NUM (f);
+}
+
+
+
+/***********
+t_send_data
+***********/
+
+static VALUE t_send_data (VALUE self, VALUE signature, VALUE data, VALUE data_length)
+{
+       int b = evma_send_data_to_connection (NUM2ULONG (signature), StringValuePtr (data), FIX2INT (data_length));
+       return INT2NUM (b);
+}
+
+
+/***********
+t_start_tls
+***********/
+
+static VALUE t_start_tls (VALUE self, VALUE signature)
+{
+       evma_start_tls (NUM2ULONG (signature));
+       return Qnil;
+}
+
+/***************
+t_set_tls_parms
+***************/
+
+static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer)
+{
+       /* set_tls_parms takes a series of positional arguments for specifying such things
+        * as private keys and certificate chains.
+        * It's expected that the parameter list will grow as we add more supported features.
+        * ALL of these parameters are optional, and can be specified as empty or NULL strings.
+        */
+       evma_set_tls_parms (NUM2ULONG (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile), (verify_peer == Qtrue ? 1 : 0));
+       return Qnil;
+}
+
+/***************
+t_get_peer_cert
+***************/
+
+static VALUE t_get_peer_cert (VALUE self, VALUE signature)
+{
+       VALUE ret = Qnil;
+
+       #ifdef WITH_SSL
+       X509 *cert = NULL;
+       BUF_MEM *buf;
+       BIO *out;
+
+       cert = evma_get_peer_cert (NUM2ULONG (signature));
+
+       if (cert != NULL) {
+               out = BIO_new(BIO_s_mem());
+               PEM_write_bio_X509(out, cert);
+               BIO_get_mem_ptr(out, &buf);
+               ret = rb_str_new(buf->data, buf->length);
+               X509_free(cert);
+               BUF_MEM_free(buf);
+       }
+       #endif
+
+       return ret;
+}
+
+/**************
+t_get_peername
+**************/
+
+static VALUE t_get_peername (VALUE self, VALUE signature)
+{
+       struct sockaddr s;
+       if (evma_get_peername (NUM2ULONG (signature), &s)) {
+               return rb_str_new ((const char*)&s, sizeof(s));
+       }
+
+       return Qnil;
+}
+
+/**************
+t_get_sockname
+**************/
+
+static VALUE t_get_sockname (VALUE self, VALUE signature)
+{
+       struct sockaddr s;
+       if (evma_get_sockname (NUM2ULONG (signature), &s)) {
+               return rb_str_new ((const char*)&s, sizeof(s));
+       }
+
+       return Qnil;
+}
+
+/********************
+t_get_subprocess_pid
+********************/
+
+static VALUE t_get_subprocess_pid (VALUE self, VALUE signature)
+{
+       pid_t pid;
+       if (evma_get_subprocess_pid (NUM2ULONG (signature), &pid)) {
+               return INT2NUM (pid);
+       }
+
+       return Qnil;
+}
+
+/***********************
+t_get_subprocess_status
+***********************/
+
+static VALUE t_get_subprocess_status (VALUE self, VALUE signature)
+{
+       VALUE proc_status = Qnil;
+
+       int status;
+       pid_t pid;
+
+       if (evma_get_subprocess_status (NUM2ULONG (signature), &status)) {
+               if (evma_get_subprocess_pid (NUM2ULONG (signature), &pid)) {
+                       proc_status = rb_obj_alloc(rb_cProcStatus);
+                       rb_iv_set(proc_status, "status", INT2FIX(status));
+                       rb_iv_set(proc_status, "pid", INT2FIX(pid));
+               }
+       }
+
+       return proc_status;
+}
+
+/**********************
+t_get_connection_count
+**********************/
+
+static VALUE t_get_connection_count (VALUE self)
+{
+       return INT2NUM(evma_get_connection_count());
+}
+
+/*****************************
+t_get_comm_inactivity_timeout
+*****************************/
+
+static VALUE t_get_comm_inactivity_timeout (VALUE self, VALUE signature)
+{
+       return rb_float_new(evma_get_comm_inactivity_timeout(NUM2ULONG (signature)));
+}
+
+/*****************************
+t_set_comm_inactivity_timeout
+*****************************/
+
+static VALUE t_set_comm_inactivity_timeout (VALUE self, VALUE signature, VALUE timeout)
+{
+       float ti = RFLOAT_VALUE(timeout);
+       if (evma_set_comm_inactivity_timeout (NUM2ULONG (signature), ti));
+               return Qtrue;
+       return Qfalse;
+}
+
+/*****************************
+t_get_pending_connect_timeout
+*****************************/
+
+static VALUE t_get_pending_connect_timeout (VALUE self, VALUE signature)
+{
+       return rb_float_new(evma_get_pending_connect_timeout(NUM2ULONG (signature)));
+}
+
+/*****************************
+t_set_pending_connect_timeout
+*****************************/
+
+static VALUE t_set_pending_connect_timeout (VALUE self, VALUE signature, VALUE timeout)
+{
+       float ti = RFLOAT_VALUE(timeout);
+       if (evma_set_pending_connect_timeout (NUM2ULONG (signature), ti));
+               return Qtrue;
+       return Qfalse;
+}
+
+/***************
+t_send_datagram
+***************/
+
+static VALUE t_send_datagram (VALUE self, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port)
+{
+       int b = evma_send_datagram (NUM2ULONG (signature), StringValuePtr (data), FIX2INT (data_length), StringValuePtr(address), FIX2INT(port));
+       return INT2NUM (b);
+}
+
+
+/******************
+t_close_connection
+******************/
+
+static VALUE t_close_connection (VALUE self, VALUE signature, VALUE after_writing)
+{
+       evma_close_connection (NUM2ULONG (signature), ((after_writing == Qtrue) ? 1 : 0));
+       return Qnil;
+}
+
+/********************************
+t_report_connection_error_status
+********************************/
+
+static VALUE t_report_connection_error_status (VALUE self, VALUE signature)
+{
+       int b = evma_report_connection_error_status (NUM2ULONG (signature));
+       return INT2NUM (b);
+}
+
+
+
+/****************
+t_connect_server
+****************/
+
+static VALUE t_connect_server (VALUE self, VALUE server, VALUE port)
+{
+       // Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
+       // Specifically, if the value of port comes in as a string rather than an integer,
+       // NUM2INT will throw a type error, but FIX2INT will generate garbage.
+
+       try {
+               const unsigned long f = evma_connect_to_server (NULL, 0, StringValuePtr(server), NUM2INT(port));
+               if (!f)
+                       rb_raise (EM_eConnectionError, "no connection");
+               return ULONG2NUM (f);
+       } catch (std::runtime_error e) {
+               rb_raise (EM_eConnectionError, e.what());
+       }
+}
+
+/*********************
+t_bind_connect_server
+*********************/
+
+static VALUE t_bind_connect_server (VALUE self, VALUE bind_addr, VALUE bind_port, VALUE server, VALUE port)
+{
+       // Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
+       // Specifically, if the value of port comes in as a string rather than an integer,
+       // NUM2INT will throw a type error, but FIX2INT will generate garbage.
+
+       try {
+               const unsigned long f = evma_connect_to_server (StringValuePtr(bind_addr), NUM2INT(bind_port), StringValuePtr(server), NUM2INT(port));
+               if (!f)
+                       rb_raise (EM_eConnectionError, "no connection");
+               return ULONG2NUM (f);
+       } catch (std::runtime_error e) {
+               rb_raise (EM_eConnectionError, e.what());
+       }
+}
+
+/*********************
+t_connect_unix_server
+*********************/
+
+static VALUE t_connect_unix_server (VALUE self, VALUE serversocket)
+{
+       const unsigned long f = evma_connect_to_unix_server (StringValuePtr(serversocket));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no connection");
+       return ULONG2NUM (f);
+}
+
+/***********
+t_attach_fd
+***********/
+
+static VALUE t_attach_fd (VALUE self, VALUE file_descriptor, VALUE watch_mode)
+{
+       const unsigned long f = evma_attach_fd (NUM2INT(file_descriptor), watch_mode == Qtrue);
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no connection");
+       return ULONG2NUM (f);
+}
+
+/***********
+t_detach_fd
+***********/
+
+static VALUE t_detach_fd (VALUE self, VALUE signature)
+{
+       return INT2NUM(evma_detach_fd (NUM2ULONG (signature)));
+}
+
+/**************
+t_get_sock_opt
+**************/
+
+static VALUE t_get_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optname)
+{
+       int fd = evma_get_file_descriptor (NUM2ULONG (signature));
+       int level = NUM2INT(lev), option = NUM2INT(optname);
+       socklen_t len = 128;
+       char buf[128];
+
+       if (getsockopt(fd, level, option, buf, &len) < 0)
+               rb_sys_fail("getsockopt");
+
+       return rb_str_new(buf, len);
+}
+
+/********************
+t_is_notify_readable
+********************/
+
+static VALUE t_is_notify_readable (VALUE self, VALUE signature)
+{
+       return evma_is_notify_readable(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+}
+
+/*********************
+t_set_notify_readable
+*********************/
+
+static VALUE t_set_notify_readable (VALUE self, VALUE signature, VALUE mode)
+{
+       evma_set_notify_readable(NUM2ULONG (signature), mode == Qtrue);
+       return Qnil;
+}
+
+/********************
+t_is_notify_readable
+********************/
+
+static VALUE t_is_notify_writable (VALUE self, VALUE signature)
+{
+       return evma_is_notify_writable(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+}
+
+/*********************
+t_set_notify_writable
+*********************/
+
+static VALUE t_set_notify_writable (VALUE self, VALUE signature, VALUE mode)
+{
+       evma_set_notify_writable(NUM2ULONG (signature), mode == Qtrue);
+       return Qnil;
+}
+
+/*******
+t_pause
+*******/
+
+static VALUE t_pause (VALUE self, VALUE signature)
+{
+       return evma_pause(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+}
+
+/********
+t_resume
+********/
+
+static VALUE t_resume (VALUE self, VALUE signature)
+{
+       return evma_resume(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+}
+
+/**********
+t_paused_p
+**********/
+
+static VALUE t_paused_p (VALUE self, VALUE signature)
+{
+       return evma_is_paused(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+}
+
+/*****************
+t_open_udp_socket
+*****************/
+
+static VALUE t_open_udp_socket (VALUE self, VALUE server, VALUE port)
+{
+       const unsigned long f = evma_open_datagram_socket (StringValuePtr(server), FIX2INT(port));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no datagram socket");
+       return ULONG2NUM (f);
+}
+
+
+
+/*****************
+t_release_machine
+*****************/
+
+static VALUE t_release_machine (VALUE self)
+{
+       evma_release_library();
+       return Qnil;
+}
+
+
+/******
+t_stop
+******/
+
+static VALUE t_stop (VALUE self)
+{
+       evma_stop_machine();
+       return Qnil;
+}
+
+/******************
+t_signal_loopbreak
+******************/
+
+static VALUE t_signal_loopbreak (VALUE self)
+{
+       evma_signal_loopbreak();
+       return Qnil;
+}
+
+/**************
+t_library_type
+**************/
+
+static VALUE t_library_type (VALUE self)
+{
+       return rb_eval_string (":extension");
+}
+
+
+
+/*******************
+t_set_timer_quantum
+*******************/
+
+static VALUE t_set_timer_quantum (VALUE self, VALUE interval)
+{
+  evma_set_timer_quantum (FIX2INT (interval));
+  return Qnil;
+}
+
+/********************
+t_get_max_timer_count
+********************/
+
+static VALUE t_get_max_timer_count (VALUE self)
+{
+  return INT2FIX (evma_get_max_timer_count());
+}
+
+/********************
+t_set_max_timer_count
+********************/
+
+static VALUE t_set_max_timer_count (VALUE self, VALUE ct)
+{
+  evma_set_max_timer_count (FIX2INT (ct));
+  return Qnil;
+}
+
+/***************
+t_setuid_string
+***************/
+
+static VALUE t_setuid_string (VALUE self, VALUE username)
+{
+  evma_setuid_string (StringValuePtr (username));
+  return Qnil;
+}
+
+
+
+/*************
+t__write_file
+*************/
+
+static VALUE t__write_file (VALUE self, VALUE filename)
+{
+       const unsigned long f = evma__write_file (StringValuePtr (filename));
+       if (!f)
+               rb_raise (rb_eRuntimeError, "file not opened");
+       return ULONG2NUM (f);
+}
+
+/**************
+t_invoke_popen
+**************/
+
+static VALUE t_invoke_popen (VALUE self, VALUE cmd)
+{
+       // 1.8.7+
+       #ifdef RARRAY_LEN
+               int len = RARRAY_LEN(cmd);
+       #else
+               int len = RARRAY (cmd)->len;
+       #endif
+       if (len > 98)
+               rb_raise (rb_eRuntimeError, "too many arguments to popen");
+       char *strings [100];
+       for (int i=0; i < len; i++) {
+               VALUE ix = INT2FIX (i);
+               VALUE s = rb_ary_aref (1, &ix, cmd);
+               strings[i] = StringValuePtr (s);
+       }
+       strings[len] = NULL;
+
+       const unsigned long f = evma_popen (strings);
+       if (!f) {
+               char *err = strerror (errno);
+               char buf[100];
+               memset (buf, 0, sizeof(buf));
+               snprintf (buf, sizeof(buf)-1, "no popen: %s", (err?err:"???"));
+               rb_raise (rb_eRuntimeError, "%s", buf);
+       }
+       return ULONG2NUM (f);
+}
+
+
+/***************
+t_read_keyboard
+***************/
+
+static VALUE t_read_keyboard (VALUE self)
+{
+       const unsigned long f = evma_open_keyboard();
+       if (!f)
+               rb_raise (rb_eRuntimeError, "no keyboard reader");
+       return ULONG2NUM (f);
+}
+
+
+/****************
+t_watch_filename
+****************/
+
+static VALUE t_watch_filename (VALUE self, VALUE fname)
+{
+       try {
+               return ULONG2NUM(evma_watch_filename(StringValuePtr(fname)));
+       } catch (std::runtime_error e) {
+               rb_sys_fail(e.what());
+       }
+}
+
+
+/******************
+t_unwatch_filename
+******************/
+
+static VALUE t_unwatch_filename (VALUE self, VALUE sig)
+{
+       evma_unwatch_filename(NUM2ULONG (sig));
+       return Qnil;
+}
+
+
+/***********
+t_watch_pid
+***********/
+
+static VALUE t_watch_pid (VALUE self, VALUE pid)
+{
+       try {
+               return ULONG2NUM(evma_watch_pid(NUM2INT(pid)));
+       } catch (std::runtime_error e) {
+               rb_sys_fail(e.what());
+       }
+}
+
+
+/*************
+t_unwatch_pid
+*************/
+
+static VALUE t_unwatch_pid (VALUE self, VALUE sig)
+{
+       evma_unwatch_pid(NUM2ULONG (sig));
+       return Qnil;
+}
+
+
+/**********
+t__epoll_p
+**********/
+
+static VALUE t__epoll_p (VALUE self)
+{
+  #ifdef HAVE_EPOLL
+  return Qtrue;
+  #else
+  return Qfalse;
+  #endif
+}
+
+/********
+t__epoll
+********/
+
+static VALUE t__epoll (VALUE self)
+{
+       evma_set_epoll (1);
+       return Qtrue;
+}
+
+/***********
+t__epoll_set
+***********/
+
+static VALUE t__epoll_set (VALUE self, VALUE val)
+{
+       if (t__epoll_p(self) == Qfalse)
+               rb_raise (EM_eUnsupported, "epoll is not supported on this platform");
+
+       evma_set_epoll (val == Qtrue ? 1 : 0);
+       return val;
+}
+
+
+/***********
+t__kqueue_p
+***********/
+
+static VALUE t__kqueue_p (VALUE self)
+{
+  #ifdef HAVE_KQUEUE
+  return Qtrue;
+  #else
+  return Qfalse;
+  #endif
+}
+
+/*********
+t__kqueue
+*********/
+
+static VALUE t__kqueue (VALUE self)
+{
+       evma_set_kqueue (1);
+       return Qtrue;
+}
+
+/*************
+t__kqueue_set
+*************/
+
+static VALUE t__kqueue_set (VALUE self, VALUE val)
+{
+       if (t__kqueue_p(self) == Qfalse)
+               rb_raise (EM_eUnsupported, "kqueue is not supported on this platform");
+
+       evma_set_kqueue (val == Qtrue ? 1 : 0);
+       return val;
+}
+
+
+/********
+t__ssl_p
+********/
+
+static VALUE t__ssl_p (VALUE self)
+{
+  #ifdef WITH_SSL
+  return Qtrue;
+  #else
+  return Qfalse;
+  #endif
+}
+
+
+/****************
+t_send_file_data
+****************/
+
+static VALUE t_send_file_data (VALUE self, VALUE signature, VALUE filename)
+{
+
+       /* The current implementation of evma_send_file_data_to_connection enforces a strict
+        * upper limit on the file size it will transmit (currently 32K). The function returns
+        * zero on success, -1 if the requested file exceeds its size limit, and a positive
+        * number for other errors.
+        * TODO: Positive return values are actually errno's, which is probably the wrong way to
+        * do this. For one thing it's ugly. For another, we can't be sure zero is never a real errno.
+        */
+
+       int b = evma_send_file_data_to_connection (NUM2ULONG (signature), StringValuePtr(filename));
+       if (b == -1)
+               rb_raise(rb_eRuntimeError, "File too large.  send_file_data() supports files under 32k.");
+       if (b > 0) {
+               char *err = strerror (b);
+               char buf[1024];
+               memset (buf, 0, sizeof(buf));
+               snprintf (buf, sizeof(buf)-1, ": %s %s", StringValuePtr(filename),(err?err:"???"));
+
+               rb_raise (rb_eIOError, "%s", buf);
+       }
+
+       return INT2NUM (0);
+}
+
+
+/*******************
+t_set_rlimit_nofile
+*******************/
+
+static VALUE t_set_rlimit_nofile (VALUE self, VALUE arg)
+{
+       arg = (NIL_P(arg)) ? -1 : NUM2INT (arg);
+       return INT2NUM (evma_set_rlimit_nofile (arg));
+}
+
+/***************************
+conn_get_outbound_data_size
+***************************/
+
+static VALUE conn_get_outbound_data_size (VALUE self)
+{
+       VALUE sig = rb_ivar_get (self, Intern_at_signature);
+       return INT2NUM (evma_get_outbound_data_size (NUM2ULONG (sig)));
+}
+
+
+/******************************
+conn_associate_callback_target
+******************************/
+
+static VALUE conn_associate_callback_target (VALUE self, VALUE sig)
+{
+       // No-op for the time being.
+       return Qnil;
+}
+
+
+/***************
+t_get_loop_time
+****************/
+
+static VALUE t_get_loop_time (VALUE self)
+{
+#ifndef HAVE_RB_TIME_NEW
+  static VALUE cTime = rb_path2class("Time");
+  static ID at = rb_intern("at");
+#endif
+
+  if (gCurrentLoopTime != 0) {
+#ifndef HAVE_RB_TIME_NEW
+    return rb_funcall(cTime, at, 2, INT2NUM(gCurrentLoopTime / 1000000), INT2NUM(gCurrentLoopTime % 1000000));
+#else
+    return rb_time_new(gCurrentLoopTime / 1000000, gCurrentLoopTime % 1000000);
+#endif
+  }
+  return Qnil;
+}
+
+
+/*************
+t_start_proxy
+**************/
+
+static VALUE t_start_proxy (VALUE self, VALUE from, VALUE to, VALUE bufsize)
+{
+       evma_start_proxy(NUM2ULONG (from), NUM2ULONG (to), NUM2ULONG(bufsize));
+       return Qnil;
+}
+
+
+/************
+t_stop_proxy
+*************/
+
+static VALUE t_stop_proxy (VALUE self, VALUE from)
+{
+       evma_stop_proxy(NUM2ULONG (from));
+       return Qnil;
+}
+
+
+/************************
+t_get_heartbeat_interval
+*************************/
+
+static VALUE t_get_heartbeat_interval (VALUE self)
+{
+       return rb_float_new(evma_get_heartbeat_interval());
+}
+
+
+/************************
+t_set_heartbeat_interval
+*************************/
+
+static VALUE t_set_heartbeat_interval (VALUE self, VALUE interval)
+{
+       float iv = RFLOAT_VALUE(interval);
+       if (evma_set_heartbeat_interval(iv))
+               return Qtrue;
+       return Qfalse;
+}
+
+
+/*********************
+Init_rubyeventmachine
+*********************/
+
+extern "C" void Init_rubyeventmachine()
+{
+       // Lookup Process::Status for get_subprocess_status
+       VALUE rb_mProcess = rb_const_get(rb_cObject, rb_intern("Process"));
+       rb_cProcStatus = rb_const_get(rb_mProcess, rb_intern("Status"));
+
+       // Tuck away some symbol values so we don't have to look 'em up every time we need 'em.
+       Intern_at_signature = rb_intern ("@signature");
+       Intern_at_timers = rb_intern ("@timers");
+       Intern_at_conns = rb_intern ("@conns");
+       Intern_at_error_handler = rb_intern("@error_handler");
+
+       Intern_event_callback = rb_intern ("event_callback");
+       Intern_run_deferred_callbacks = rb_intern ("run_deferred_callbacks");
+       Intern_delete = rb_intern ("delete");
+       Intern_call = rb_intern ("call");
+       Intern_receive_data = rb_intern ("receive_data");
+       Intern_ssl_handshake_completed = rb_intern ("ssl_handshake_completed");
+       Intern_ssl_verify_peer = rb_intern ("ssl_verify_peer");
+       Intern_notify_readable = rb_intern ("notify_readable");
+       Intern_notify_writable = rb_intern ("notify_writable");
+       Intern_proxy_target_unbound = rb_intern ("proxy_target_unbound");
+
+       // INCOMPLETE, we need to define class Connections inside module EventMachine
+       // run_machine and run_machine_without_threads are now identical.
+       // Must deprecate the without_threads variant.
+       EmModule = rb_define_module ("EventMachine");
+       EmConnection = rb_define_class_under (EmModule, "Connection", rb_cObject);
+
+       rb_define_class_under (EmModule, "NoHandlerForAcceptedConnection", rb_eRuntimeError);
+       EM_eConnectionError = rb_define_class_under (EmModule, "ConnectionError", rb_eRuntimeError);
+       EM_eConnectionNotBound = rb_define_class_under (EmModule, "ConnectionNotBound", rb_eRuntimeError);
+       EM_eUnknownTimerFired = rb_define_class_under (EmModule, "UnknownTimerFired", rb_eRuntimeError);
+       EM_eUnsupported = rb_define_class_under (EmModule, "Unsupported", rb_eRuntimeError);
+
+       rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0);
+       rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine_without_threads, 0);
+       rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine_without_threads, 0);
+       rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1);
+       rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2);
+       rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1);
+       rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1);
+       rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4);
+       rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1);
+       rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1);
+       rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3);
+       rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5);
+       rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2);
+       rb_define_module_function (EmModule, "report_connection_error_status", (VALUE(*)(...))t_report_connection_error_status, 1);
+       rb_define_module_function (EmModule, "connect_server", (VALUE(*)(...))t_connect_server, 2);
+       rb_define_module_function (EmModule, "bind_connect_server", (VALUE(*)(...))t_bind_connect_server, 4);
+       rb_define_module_function (EmModule, "connect_unix_server", (VALUE(*)(...))t_connect_unix_server, 1);
+
+       rb_define_module_function (EmModule, "attach_fd", (VALUE (*)(...))t_attach_fd, 2);
+       rb_define_module_function (EmModule, "detach_fd", (VALUE (*)(...))t_detach_fd, 1);
+       rb_define_module_function (EmModule, "get_sock_opt", (VALUE (*)(...))t_get_sock_opt, 3);
+       rb_define_module_function (EmModule, "set_notify_readable", (VALUE (*)(...))t_set_notify_readable, 2);
+       rb_define_module_function (EmModule, "set_notify_writable", (VALUE (*)(...))t_set_notify_writable, 2);
+       rb_define_module_function (EmModule, "is_notify_readable", (VALUE (*)(...))t_is_notify_readable, 1);
+       rb_define_module_function (EmModule, "is_notify_writable", (VALUE (*)(...))t_is_notify_writable, 1);
+
+       rb_define_module_function (EmModule, "pause_connection", (VALUE (*)(...))t_pause, 1);
+       rb_define_module_function (EmModule, "resume_connection", (VALUE (*)(...))t_resume, 1);
+       rb_define_module_function (EmModule, "connection_paused?", (VALUE (*)(...))t_paused_p, 1);
+
+       rb_define_module_function (EmModule, "start_proxy", (VALUE (*)(...))t_start_proxy, 3);
+       rb_define_module_function (EmModule, "stop_proxy", (VALUE (*)(...))t_stop_proxy, 1);
+
+       rb_define_module_function (EmModule, "watch_filename", (VALUE (*)(...))t_watch_filename, 1);
+       rb_define_module_function (EmModule, "unwatch_filename", (VALUE (*)(...))t_unwatch_filename, 1);
+
+       rb_define_module_function (EmModule, "watch_pid", (VALUE (*)(...))t_watch_pid, 1);
+       rb_define_module_function (EmModule, "unwatch_pid", (VALUE (*)(...))t_unwatch_pid, 1);
+
+       rb_define_module_function (EmModule, "current_time", (VALUE(*)(...))t_get_loop_time, 0);
+
+       rb_define_module_function (EmModule, "open_udp_socket", (VALUE(*)(...))t_open_udp_socket, 2);
+       rb_define_module_function (EmModule, "read_keyboard", (VALUE(*)(...))t_read_keyboard, 0);
+       rb_define_module_function (EmModule, "release_machine", (VALUE(*)(...))t_release_machine, 0);
+       rb_define_module_function (EmModule, "stop", (VALUE(*)(...))t_stop, 0);
+       rb_define_module_function (EmModule, "signal_loopbreak", (VALUE(*)(...))t_signal_loopbreak, 0);
+       rb_define_module_function (EmModule, "library_type", (VALUE(*)(...))t_library_type, 0);
+       rb_define_module_function (EmModule, "set_timer_quantum", (VALUE(*)(...))t_set_timer_quantum, 1);
+       rb_define_module_function (EmModule, "get_max_timer_count", (VALUE(*)(...))t_get_max_timer_count, 0);
+       rb_define_module_function (EmModule, "set_max_timer_count", (VALUE(*)(...))t_set_max_timer_count, 1);
+       rb_define_module_function (EmModule, "setuid_string", (VALUE(*)(...))t_setuid_string, 1);
+       rb_define_module_function (EmModule, "invoke_popen", (VALUE(*)(...))t_invoke_popen, 1);
+       rb_define_module_function (EmModule, "send_file_data", (VALUE(*)(...))t_send_file_data, 2);
+       rb_define_module_function (EmModule, "get_heartbeat_interval", (VALUE(*)(...))t_get_heartbeat_interval, 0);
+       rb_define_module_function (EmModule, "set_heartbeat_interval", (VALUE(*)(...))t_set_heartbeat_interval, 1);
+
+       // Provisional:
+       rb_define_module_function (EmModule, "_write_file", (VALUE(*)(...))t__write_file, 1);
+
+       rb_define_module_function (EmModule, "get_peername", (VALUE(*)(...))t_get_peername, 1);
+       rb_define_module_function (EmModule, "get_sockname", (VALUE(*)(...))t_get_sockname, 1);
+       rb_define_module_function (EmModule, "get_subprocess_pid", (VALUE(*)(...))t_get_subprocess_pid, 1);
+       rb_define_module_function (EmModule, "get_subprocess_status", (VALUE(*)(...))t_get_subprocess_status, 1);
+       rb_define_module_function (EmModule, "get_comm_inactivity_timeout", (VALUE(*)(...))t_get_comm_inactivity_timeout, 1);
+       rb_define_module_function (EmModule, "set_comm_inactivity_timeout", (VALUE(*)(...))t_set_comm_inactivity_timeout, 2);
+       rb_define_module_function (EmModule, "get_pending_connect_timeout", (VALUE(*)(...))t_get_pending_connect_timeout, 1);
+       rb_define_module_function (EmModule, "set_pending_connect_timeout", (VALUE(*)(...))t_set_pending_connect_timeout, 2);
+       rb_define_module_function (EmModule, "set_rlimit_nofile", (VALUE(*)(...))t_set_rlimit_nofile, 1);
+       rb_define_module_function (EmModule, "get_connection_count", (VALUE(*)(...))t_get_connection_count, 0);
+
+       rb_define_module_function (EmModule, "epoll", (VALUE(*)(...))t__epoll, 0);
+       rb_define_module_function (EmModule, "epoll=", (VALUE(*)(...))t__epoll_set, 1);
+       rb_define_module_function (EmModule, "epoll?", (VALUE(*)(...))t__epoll_p, 0);
+
+       rb_define_module_function (EmModule, "kqueue", (VALUE(*)(...))t__kqueue, 0);
+       rb_define_module_function (EmModule, "kqueue=", (VALUE(*)(...))t__kqueue_set, 1);
+       rb_define_module_function (EmModule, "kqueue?", (VALUE(*)(...))t__kqueue_p, 0);
+
+       rb_define_module_function (EmModule, "ssl?", (VALUE(*)(...))t__ssl_p, 0);
+
+       rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0);
+       rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1);
+
+       rb_define_const (EmModule, "TimerFired", INT2NUM(100));
+       rb_define_const (EmModule, "ConnectionData", INT2NUM(101));
+       rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(102));
+       rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(103));
+       rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(104));
+       rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(105));
+
+       rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(106));
+       rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(107));
+
+       rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(108));
+
+}
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.cpp
new file mode 100644 (file)
index 0000000..54bcf00
--- /dev/null
@@ -0,0 +1,89 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     sigs.cpp\r
+Date:     06Apr06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+#include "project.h"\r
+\r
+\r
+bool gTerminateSignalReceived;\r
+\r
+\r
+/**************\r
+SigtermHandler\r
+**************/\r
+\r
+void SigtermHandler (int sig)\r
+{\r
+       // This is a signal-handler, don't do anything frisky. Interrupts are disabled.\r
+       // Set the terminate flag WITHOUT trying to lock a mutex- otherwise we can easily\r
+       // self-deadlock, especially if the event machine is looping quickly.\r
+       gTerminateSignalReceived = true;\r
+}\r
+\r
+\r
+/*********************\r
+InstallSignalHandlers\r
+*********************/\r
+\r
+void InstallSignalHandlers()\r
+{\r
+       #ifdef OS_UNIX\r
+       static bool bInstalled = false;\r
+       if (!bInstalled) {\r
+               bInstalled = true;\r
+               signal (SIGINT, SigtermHandler);\r
+               signal (SIGTERM, SigtermHandler);\r
+               signal (SIGPIPE, SIG_IGN);\r
+       }\r
+       #endif\r
+}\r
+\r
+\r
+\r
+/*******************\r
+WintelSignalHandler\r
+*******************/\r
+\r
+#ifdef OS_WIN32\r
+BOOL WINAPI WintelSignalHandler (DWORD control)\r
+{\r
+       if (control == CTRL_C_EVENT)\r
+               gTerminateSignalReceived = true;\r
+       return TRUE;\r
+}\r
+#endif\r
+\r
+/************\r
+HookControlC\r
+************/\r
+\r
+#ifdef OS_WIN32\r
+void HookControlC (bool hook)\r
+{\r
+       if (hook) {\r
+               // INSTALL hook\r
+               SetConsoleCtrlHandler (WintelSignalHandler, TRUE);\r
+       }\r
+       else {\r
+               // UNINSTALL hook\r
+               SetConsoleCtrlHandler (WintelSignalHandler, FALSE);\r
+       }\r
+}\r
+#endif\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/sigs.h
new file mode 100644 (file)
index 0000000..edfaadf
--- /dev/null
@@ -0,0 +1,32 @@
+/*****************************************************************************\r
+\r
+$Id$\r
+\r
+File:     sigs.h\r
+Date:     06Apr06\r
+\r
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+Gmail: blackhedd\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of either: 1) the GNU General Public License\r
+as published by the Free Software Foundation; either version 2 of the\r
+License, or (at your option) any later version; or 2) Ruby's License.\r
+\r
+See the file COPYING for complete licensing information.\r
+\r
+*****************************************************************************/\r
+\r
+\r
+#ifndef __Signals__H_\r
+#define __Signals__H_\r
+\r
+void InstallSignalHandlers();\r
+extern bool gTerminateSignalReceived;\r
+\r
+#ifdef OS_WIN32\r
+void HookControlC (bool);\r
+#endif\r
+\r
+#endif // __Signals__H_\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.cpp b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.cpp
new file mode 100644 (file)
index 0000000..2f97833
--- /dev/null
@@ -0,0 +1,460 @@
+/*****************************************************************************
+
+$Id$
+
+File:     ssl.cpp
+Date:     30Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+#ifdef WITH_SSL
+
+#include "project.h"
+
+
+bool SslContext_t::bLibraryInitialized = false;
+
+
+
+static void InitializeDefaultCredentials();
+static EVP_PKEY *DefaultPrivateKey = NULL;
+static X509 *DefaultCertificate = NULL;
+
+static char PrivateMaterials[] = {
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
+"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
+"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
+"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
+"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
+"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
+"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
+"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
+"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
+"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
+"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
+"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
+"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
+"-----END RSA PRIVATE KEY-----\n"
+"-----BEGIN CERTIFICATE-----\n"
+"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
+"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
+"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
+"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
+"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
+"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
+"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
+"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
+"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
+"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
+"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
+"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
+"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
+"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
+"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
+"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
+"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
+"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
+"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
+"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
+"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
+"-----END CERTIFICATE-----\n"};
+
+/* These private materials were made with:
+ * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
+ * TODO: We need a full-blown capability to work with user-supplied
+ * keypairs and properly-signed certificates.
+ */
+
+
+/*****************
+builtin_passwd_cb
+*****************/
+
+extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata)
+{
+       strcpy (buf, "kittycat");
+       return 8;
+}
+
+/****************************
+InitializeDefaultCredentials
+****************************/
+
+static void InitializeDefaultCredentials()
+{
+       BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
+       assert (bio);
+
+       if (DefaultPrivateKey) {
+               // we may come here in a restart.
+               EVP_PKEY_free (DefaultPrivateKey);
+               DefaultPrivateKey = NULL;
+       }
+       PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
+
+       if (DefaultCertificate) {
+               // we may come here in a restart.
+               X509_free (DefaultCertificate);
+               DefaultCertificate = NULL;
+       }
+       PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
+
+       BIO_free (bio);
+}
+
+
+
+/**************************
+SslContext_t::SslContext_t
+**************************/
+
+SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile):
+       pCtx (NULL),
+       PrivateKey (NULL),
+       Certificate (NULL)
+{
+       /* TODO: the usage of the specified private-key and cert-chain filenames only applies to
+        * client-side connections at this point. Server connections currently use the default materials.
+        * That needs to be fixed asap.
+        * Also, in this implementation, server-side connections use statically defined X-509 defaults.
+        * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
+        * objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
+        */
+
+       if (!bLibraryInitialized) {
+               bLibraryInitialized = true;
+               SSL_library_init();
+               OpenSSL_add_ssl_algorithms();
+               OpenSSL_add_all_algorithms();
+               SSL_load_error_strings();
+               ERR_load_crypto_strings();
+
+               InitializeDefaultCredentials();
+       }
+
+       bIsServer = is_server;
+       pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method());
+       if (!pCtx)
+               throw std::runtime_error ("no SSL context");
+
+       SSL_CTX_set_options (pCtx, SSL_OP_ALL);
+       //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3));
+
+       if (is_server) {
+               // The SSL_CTX calls here do NOT allocate memory.
+               int e;
+               if (privkeyfile.length() > 0)
+                       e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
+               else
+                       e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
+               assert (e > 0);
+               if (certchainfile.length() > 0)
+                       e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
+               else
+                       e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
+               assert (e > 0);
+       }
+
+       SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
+
+       if (is_server) {
+               SSL_CTX_sess_set_cache_size (pCtx, 128);
+               SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
+       }
+       else {
+               int e;
+               if (privkeyfile.length() > 0) {
+                       e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
+                       assert (e > 0);
+               }
+               if (certchainfile.length() > 0) {
+                       e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
+                       assert (e > 0);
+               }
+       }
+}
+
+
+
+/***************************
+SslContext_t::~SslContext_t
+***************************/
+
+SslContext_t::~SslContext_t()
+{
+       if (pCtx)
+               SSL_CTX_free (pCtx);
+       if (PrivateKey)
+               EVP_PKEY_free (PrivateKey);
+       if (Certificate)
+               X509_free (Certificate);
+}
+
+
+
+/******************
+SslBox_t::SslBox_t
+******************/
+
+SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding):
+       bIsServer (is_server),
+       bHandshakeCompleted (false),
+       bVerifyPeer (verify_peer),
+       pSSL (NULL),
+       pbioRead (NULL),
+       pbioWrite (NULL)
+{
+       /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
+        * a new one every time we come here.
+        */
+
+       Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
+       assert (Context);
+
+       pbioRead = BIO_new (BIO_s_mem());
+       assert (pbioRead);
+
+       pbioWrite = BIO_new (BIO_s_mem());
+       assert (pbioWrite);
+
+       pSSL = SSL_new (Context->pCtx);
+       assert (pSSL);
+       SSL_set_bio (pSSL, pbioRead, pbioWrite);
+
+       // Store a pointer to the binding signature in the SSL object so we can retrieve it later
+       SSL_set_ex_data(pSSL, 0, (void*) binding);
+
+       if (bVerifyPeer)
+               SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
+
+       if (!bIsServer)
+               SSL_connect (pSSL);
+}
+
+
+
+/*******************
+SslBox_t::~SslBox_t
+*******************/
+
+SslBox_t::~SslBox_t()
+{
+       // Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
+       if (pSSL) {
+               if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
+                       SSL_shutdown (pSSL);
+               else
+                       SSL_clear (pSSL);
+               SSL_free (pSSL);
+       }
+
+       delete Context;
+}
+
+
+
+/***********************
+SslBox_t::PutCiphertext
+***********************/
+
+bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
+{
+       assert (buf && (bufsize > 0));
+
+       assert (pbioRead);
+       int n = BIO_write (pbioRead, buf, bufsize);
+
+       return (n == bufsize) ? true : false;
+}
+
+
+/**********************
+SslBox_t::GetPlaintext
+**********************/
+
+int SslBox_t::GetPlaintext (char *buf, int bufsize)
+{
+       if (!SSL_is_init_finished (pSSL)) {
+               int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
+               if (e < 0) {
+                       int er = SSL_get_error (pSSL, e);
+                       if (er != SSL_ERROR_WANT_READ) {
+                               // Return -1 for a nonfatal error, -2 for an error that should force the connection down.
+                               return (er == SSL_ERROR_SSL) ? (-2) : (-1);
+                       }
+                       else
+                               return 0;
+               }
+               bHandshakeCompleted = true;
+               // If handshake finished, FALL THROUGH and return the available plaintext.
+       }
+
+       if (!SSL_is_init_finished (pSSL)) {
+               // We can get here if a browser abandons a handshake.
+               // The user can see a warning dialog and abort the connection.
+               cerr << "<SSL_incomp>";
+               return 0;
+       }
+
+       //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
+
+       int n = SSL_read (pSSL, buf, bufsize);
+       if (n >= 0) {
+               return n;
+       }
+       else {
+               if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
+                       return 0;
+               }
+               else {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+
+/**************************
+SslBox_t::CanGetCiphertext
+**************************/
+
+bool SslBox_t::CanGetCiphertext()
+{
+       assert (pbioWrite);
+       return BIO_pending (pbioWrite) ? true : false;
+}
+
+
+
+/***********************
+SslBox_t::GetCiphertext
+***********************/
+
+int SslBox_t::GetCiphertext (char *buf, int bufsize)
+{
+       assert (pbioWrite);
+       assert (buf && (bufsize > 0));
+
+       return BIO_read (pbioWrite, buf, bufsize);
+}
+
+
+
+/**********************
+SslBox_t::PutPlaintext
+**********************/
+
+int SslBox_t::PutPlaintext (const char *buf, int bufsize)
+{
+       // The caller will interpret the return value as the number of bytes written.
+       // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
+       // from SSL_write means we should immediately retry? The socket-machine loop
+       // will probably wait for a time-out cycle (perhaps a second) before re-trying.
+       // THIS WOULD CAUSE A PERCEPTIBLE DELAY!
+
+       /* We internally queue any outbound plaintext that can't be dispatched
+        * because we're in the middle of a handshake or something.
+        * When we get called, try to send any queued data first, and then
+        * send the caller's data (or queue it). We may get called with no outbound
+        * data, which means we try to send the outbound queue and that's all.
+        *
+        * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
+        * Note that if we return 0, the connection is still considered live
+        * and we are signalling that we have accepted the outbound data (if any).
+        */
+
+       OutboundQ.Push (buf, bufsize);
+
+       if (!SSL_is_init_finished (pSSL))
+               return 0;
+
+       bool fatal = false;
+       bool did_work = false;
+
+       while (OutboundQ.HasPages()) {
+               const char *page;
+               int length;
+               OutboundQ.Front (&page, &length);
+               assert (page && (length > 0));
+               int n = SSL_write (pSSL, page, length);
+               if (n > 0) {
+                       did_work = true;
+                       OutboundQ.PopFront();
+               }
+               else {
+                       int er = SSL_get_error (pSSL, n);
+                       if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
+                               fatal = true;
+                       break;
+               }
+       }
+
+
+       if (did_work)
+               return 1;
+       else if (fatal)
+               return -1;
+       else
+               return 0;
+}
+
+/**********************
+SslBox_t::GetPeerCert
+**********************/
+
+X509 *SslBox_t::GetPeerCert()
+{
+       X509 *cert = NULL;
+
+       if (pSSL)
+               cert = SSL_get_peer_certificate(pSSL);
+
+       return cert;
+}
+
+
+/******************
+ssl_verify_wrapper
+*******************/
+
+extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
+{
+       unsigned long binding;
+       X509 *cert;
+       SSL *ssl;
+       BUF_MEM *buf;
+       BIO *out;
+       int result;
+
+       cert = X509_STORE_CTX_get_current_cert(ctx);
+       ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+       binding = (unsigned long) SSL_get_ex_data(ssl, 0);
+
+       out = BIO_new(BIO_s_mem());
+       PEM_write_bio_X509(out, cert);
+       BIO_write(out, "\0", 1);
+       BIO_get_mem_ptr(out, &buf);
+
+       ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject(binding));
+       result = (cd->VerifySslPeer(buf->data) == true ? 1 : 0);
+       BUF_MEM_free(buf);
+
+       return result;
+}
+
+#endif // WITH_SSL
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.h b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/ext/ssl.h
new file mode 100644 (file)
index 0000000..8378394
--- /dev/null
@@ -0,0 +1,94 @@
+/*****************************************************************************
+
+$Id$
+
+File:     ssl.h
+Date:     30Apr06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+#ifndef __SslBox__H_
+#define __SslBox__H_
+
+
+
+
+#ifdef WITH_SSL
+
+/******************
+class SslContext_t
+******************/
+
+class SslContext_t
+{
+       public:
+               SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
+               virtual ~SslContext_t();
+
+       private:
+               static bool bLibraryInitialized;
+
+       private:
+               bool bIsServer;
+               SSL_CTX *pCtx;
+
+               EVP_PKEY *PrivateKey;
+               X509 *Certificate;
+
+       friend class SslBox_t;
+};
+
+
+/**************
+class SslBox_t
+**************/
+
+class SslBox_t
+{
+       public:
+               SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding);
+               virtual ~SslBox_t();
+
+               int PutPlaintext (const char*, int);
+               int GetPlaintext (char*, int);
+
+               bool PutCiphertext (const char*, int);
+               bool CanGetCiphertext();
+               int GetCiphertext (char*, int);
+               bool IsHandshakeCompleted() {return bHandshakeCompleted;}
+
+               X509 *GetPeerCert();
+
+               void Shutdown();
+
+       protected:
+               SslContext_t *Context;
+
+               bool bIsServer;
+               bool bHandshakeCompleted;
+               bool bVerifyPeer;
+               SSL *pSSL;
+               BIO *pbioRead;
+               BIO *pbioWrite;
+
+               PageList OutboundQ;
+};
+
+extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);
+
+#endif // WITH_SSL
+
+
+#endif // __SslBox__H_
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.classpath b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.classpath
new file mode 100644 (file)
index 0000000..9c3bc8e
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry excluding="src/" kind="src" path=""/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>\r
+       <classpathentry kind="output" path="src"/>\r
+</classpath>\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.project b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/.project
new file mode 100644 (file)
index 0000000..6ca9626
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>em_reactor</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactor.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactor.java
new file mode 100644 (file)
index 0000000..5e92429
--- /dev/null
@@ -0,0 +1,570 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+package com.rubyeventmachine;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.nio.*;
+import java.net.*;
+import java.util.concurrent.atomic.*;
+import java.security.*;
+
+public class EmReactor {
+       public final int EM_TIMER_FIRED = 100;
+       public final int EM_CONNECTION_READ = 101;
+       public final int EM_CONNECTION_UNBOUND = 102;
+       public final int EM_CONNECTION_ACCEPTED = 103;
+       public final int EM_CONNECTION_COMPLETED = 104;
+       public final int EM_LOOPBREAK_SIGNAL = 105;
+       public final int EM_CONNECTION_NOTIFY_READABLE = 106;
+       public final int EM_CONNECTION_NOTIFY_WRITABLE = 107;
+       public final int EM_SSL_HANDSHAKE_COMPLETED = 108;
+       public final int EM_SSL_VERIFY = 109;
+       public final int EM_PROXY_TARGET_UNBOUND = 110;
+
+       private Selector mySelector;
+       private TreeMap<Long, ArrayList<Long>> Timers;
+       private HashMap<Long, EventableChannel> Connections;
+       private HashMap<Long, ServerSocketChannel> Acceptors;
+       private ArrayList<Long> NewConnections;
+       private ArrayList<Long> UnboundConnections;
+       private ArrayList<EventableSocketChannel> DetachedConnections;
+
+       private boolean bRunReactor;
+       private long BindingIndex;
+       private AtomicBoolean loopBreaker;
+       private ByteBuffer myReadBuffer;
+       private int timerQuantum;
+
+       public EmReactor() {
+               Timers = new TreeMap<Long, ArrayList<Long>>();
+               Connections = new HashMap<Long, EventableChannel>();
+               Acceptors = new HashMap<Long, ServerSocketChannel>();
+               NewConnections = new ArrayList<Long>();
+               UnboundConnections = new ArrayList<Long>();
+               DetachedConnections = new ArrayList<EventableSocketChannel>();
+
+               BindingIndex = 0;
+               loopBreaker = new AtomicBoolean();
+               loopBreaker.set(false);
+               myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
+               timerQuantum = 98;
+       }
+
+       /**
+        * This is a no-op stub, intended to be overridden in user code.
+        */
+       public void eventCallback (long sig, int eventType, ByteBuffer data, long data2) {
+               System.out.println ("Default callback: "+sig+" "+eventType+" "+data+" "+data2);
+       }
+       public void eventCallback (long sig, int eventType, ByteBuffer data) {
+               eventCallback (sig, eventType, data, 0);
+       }
+
+
+       public void run() {
+               try {
+                       mySelector = Selector.open();
+                       bRunReactor = true;
+               } catch (IOException e) {
+                       throw new RuntimeException ("Could not open selector", e);
+               }
+
+               while (bRunReactor) {
+                       runLoopbreaks();
+                       if (!bRunReactor) break;
+
+                       runTimers();
+                       if (!bRunReactor) break;
+
+                       removeUnboundConnections();
+                       checkIO();
+                       addNewConnections();
+                       processIO();
+               }
+
+               close();
+       }
+
+       void addNewConnections() {
+               ListIterator<EventableSocketChannel> iter = DetachedConnections.listIterator(0);
+               while (iter.hasNext()) {
+                       EventableSocketChannel ec = iter.next();
+                       ec.cleanup();
+               }
+               DetachedConnections.clear();
+
+               ListIterator<Long> iter2 = NewConnections.listIterator(0);
+               while (iter2.hasNext()) {
+                       long b = iter2.next();
+
+                       EventableChannel ec = Connections.get(b);
+                       if (ec != null) {
+                               try {
+                                       ec.register();
+                               } catch (ClosedChannelException e) {
+                                       UnboundConnections.add (ec.getBinding());
+                               }
+                       }
+               }
+               NewConnections.clear();
+       }
+
+       void removeUnboundConnections() {
+               ListIterator<Long> iter = UnboundConnections.listIterator(0);
+               while (iter.hasNext()) {
+                       long b = iter.next();
+
+                       EventableChannel ec = Connections.remove(b);
+                       if (ec != null) {
+                               eventCallback (b, EM_CONNECTION_UNBOUND, null);
+                               ec.close();
+
+                               EventableSocketChannel sc = (EventableSocketChannel) ec;
+                               if (sc != null && sc.isAttached())
+                                       DetachedConnections.add (sc);
+                       }
+               }
+               UnboundConnections.clear();
+       }
+
+       void checkIO() {
+               long timeout;
+
+               if (NewConnections.size() > 0) {
+                       timeout = -1;
+               } else if (!Timers.isEmpty()) {
+                       long now = new Date().getTime();
+                       long k = Timers.firstKey();
+                       long diff = k-now;
+
+                       if (diff <= 0)
+                               timeout = -1; // don't wait, just poll once
+                       else
+                               timeout = diff;
+               } else {
+                       timeout = 0; // wait indefinitely
+               }
+
+               try {
+                       if (timeout == -1)
+                               mySelector.selectNow();
+                       else
+                               mySelector.select(timeout);
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+       }
+
+       void processIO() {
+               Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
+               while (it.hasNext()) {
+                       SelectionKey k = it.next();
+                       it.remove();
+
+                       if (k.isConnectable())
+                               isConnectable(k);
+
+                       else if (k.isAcceptable())
+                               isAcceptable(k);
+
+                       else {
+                               if (k.isWritable())
+                                       isWritable(k);
+
+                               if (k.isReadable())
+                                       isReadable(k);
+                       }
+               }
+       }
+
+       void isAcceptable (SelectionKey k) {
+               ServerSocketChannel ss = (ServerSocketChannel) k.channel();
+               SocketChannel sn;
+               long b;
+
+               for (int n = 0; n < 10; n++) {
+                       try {
+                               sn = ss.accept();
+                               if (sn == null)
+                                       break;
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                               k.cancel();
+
+                               ServerSocketChannel server = Acceptors.remove(k.attachment());
+                               if (server != null)
+                                       try{ server.close(); } catch (IOException ex) {};
+                               break;
+                       }
+
+                       try {
+                               sn.configureBlocking(false);
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                               continue;
+                       }
+
+                       b = createBinding();
+                       EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
+                       Connections.put (b, ec);
+                       NewConnections.add (b);
+
+                       eventCallback (((Long)k.attachment()).longValue(), EM_CONNECTION_ACCEPTED, null, b);
+               }
+       }
+
+       void isReadable (SelectionKey k) {
+               EventableChannel ec = (EventableChannel) k.attachment();
+               long b = ec.getBinding();
+
+               if (ec.isWatchOnly()) {
+                       if (ec.isNotifyReadable())
+                               eventCallback (b, EM_CONNECTION_NOTIFY_READABLE, null);
+               } else {
+                       myReadBuffer.clear();
+
+                       try {
+                               ec.readInboundData (myReadBuffer);
+                               myReadBuffer.flip();
+                               if (myReadBuffer.limit() > 0)
+                                       eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
+                       } catch (IOException e) {
+                               UnboundConnections.add (b);
+                       }
+               }
+       }
+
+       void isWritable (SelectionKey k) {
+               EventableChannel ec = (EventableChannel) k.attachment();
+               long b = ec.getBinding();
+
+               if (ec.isWatchOnly()) {
+                       if (ec.isNotifyWritable())
+                               eventCallback (b, EM_CONNECTION_NOTIFY_WRITABLE, null);
+               }
+               else {
+                       try {
+                               if (!ec.writeOutboundData())
+                                       UnboundConnections.add (b);
+                       } catch (IOException e) {
+                               UnboundConnections.add (b);
+                       }
+               }
+       }
+
+       void isConnectable (SelectionKey k) {
+               EventableSocketChannel ec = (EventableSocketChannel) k.attachment();
+               long b = ec.getBinding();
+
+               try {
+                       if (ec.finishConnecting())
+                               eventCallback (b, EM_CONNECTION_COMPLETED, null);
+                       else
+                               UnboundConnections.add (b);
+               } catch (IOException e) {
+                       UnboundConnections.add (b);
+               }
+       }
+
+       void close() {
+               try {
+                       if (mySelector != null)
+                               mySelector.close();
+               } catch (IOException e) {}
+               mySelector = null;
+
+               // run down open connections and sockets.
+               Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
+               while (i.hasNext()) {
+                       try {
+                               i.next().close();
+                       } catch (IOException e) {}
+               }
+
+               // 29Sep09: We create an ArrayList of the existing connections, then iterate over
+               // that to call unbind on them. This is because an unbind can trigger a reconnect,
+               // which will add to the Connections HashMap, causing a ConcurrentModificationException.
+               // XXX: The correct behavior here would be to latch the various reactor methods to return
+               // immediately if the reactor is shutting down.
+               ArrayList<EventableChannel> conns = new ArrayList<EventableChannel>();
+               Iterator<EventableChannel> i2 = Connections.values().iterator();
+               while (i2.hasNext()) {
+                       EventableChannel ec = i2.next();
+                       if (ec != null) {
+                               conns.add (ec);
+                       }
+               }
+               Connections.clear();
+
+               ListIterator<EventableChannel> i3 = conns.listIterator(0);
+               while (i3.hasNext()) {
+                       EventableChannel ec = i3.next();
+                       eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, null);
+                       ec.close();
+
+                       EventableSocketChannel sc = (EventableSocketChannel) ec;
+                       if (sc != null && sc.isAttached())
+                               DetachedConnections.add (sc);
+               }
+
+               ListIterator<EventableSocketChannel> i4 = DetachedConnections.listIterator(0);
+               while (i4.hasNext()) {
+                       EventableSocketChannel ec = i4.next();
+                       ec.cleanup();
+               }
+               DetachedConnections.clear();
+       }
+
+       void runLoopbreaks() {
+               if (loopBreaker.getAndSet(false)) {
+                       eventCallback (0, EM_LOOPBREAK_SIGNAL, null);
+               }
+       }
+
+       public void stop() {
+               bRunReactor = false;
+               signalLoopbreak();
+       }
+
+       void runTimers() {
+               long now = new Date().getTime();
+               while (!Timers.isEmpty()) {
+                       long k = Timers.firstKey();
+                       if (k > now)
+                               break;
+
+                       ArrayList<Long> callbacks = Timers.get(k);
+                       Timers.remove(k);
+
+                       // Fire all timers at this timestamp
+                       ListIterator<Long> iter = callbacks.listIterator(0);
+                       while (iter.hasNext()) {
+                               eventCallback (0, EM_TIMER_FIRED, null, iter.next().longValue());
+                       }
+               }
+       }
+
+       public long installOneshotTimer (int milliseconds) {
+               long s = createBinding();
+               long deadline = new Date().getTime() + milliseconds;
+
+               if (Timers.containsKey(deadline)) {
+                       Timers.get(deadline).add(s);
+               } else {
+                       ArrayList<Long> callbacks = new ArrayList<Long>();
+                       callbacks.add(s);
+                       Timers.put(deadline, callbacks);
+               }
+
+               return s;
+       }
+
+       public long startTcpServer (SocketAddress sa) throws EmReactorException {
+               try {
+                       ServerSocketChannel server = ServerSocketChannel.open();
+                       server.configureBlocking(false);
+                       server.socket().bind (sa);
+                       long s = createBinding();
+                       Acceptors.put(s, server);
+                       server.register(mySelector, SelectionKey.OP_ACCEPT, s);
+                       return s;
+               } catch (IOException e) {
+                       throw new EmReactorException ("unable to open socket acceptor: " + e.toString());
+               }
+       }
+
+       public long startTcpServer (String address, int port) throws EmReactorException {
+               return startTcpServer (new InetSocketAddress (address, port));
+       }
+
+       public void stopTcpServer (long signature) throws IOException {
+               ServerSocketChannel server = Acceptors.remove(signature);
+               if (server != null)
+                       server.close();
+               else
+                       throw new RuntimeException ("failed to close unknown acceptor");
+       }
+
+       public long openUdpSocket (InetSocketAddress address) throws IOException {
+               // TODO, don't throw an exception out of here.
+               DatagramChannel dg = DatagramChannel.open();
+               dg.configureBlocking(false);
+               dg.socket().bind(address);
+               long b = createBinding();
+               EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
+               dg.register(mySelector, SelectionKey.OP_READ, ec);
+               Connections.put(b, ec);
+               return b;
+       }
+
+       public long openUdpSocket (String address, int port) throws IOException {
+               return openUdpSocket (new InetSocketAddress (address, port));
+       }
+
+       public void sendData (long sig, ByteBuffer bb) throws IOException {
+               Connections.get(sig).scheduleOutboundData( bb );
+       }
+
+       public void sendData (long sig, byte[] data) throws IOException {
+               sendData (sig, ByteBuffer.wrap(data));
+       }
+
+       public void setCommInactivityTimeout (long sig, long mills) {
+               Connections.get(sig).setCommInactivityTimeout (mills);
+       }
+
+       public void sendDatagram (long sig, String data, int length, String recipAddress, int recipPort) {
+               sendDatagram (sig, ByteBuffer.wrap(data.getBytes()), recipAddress, recipPort);
+       }
+
+       public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
+               (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
+       }
+
+       public long connectTcpServer (String address, int port) {
+               return connectTcpServer(null, 0, address, port);
+       }
+
+       public long connectTcpServer (String bindAddr, int bindPort, String address, int port) {
+               long b = createBinding();
+
+               try {
+                       SocketChannel sc = SocketChannel.open();
+                       sc.configureBlocking(false);
+                       if (bindAddr != null)
+                               sc.socket().bind(new InetSocketAddress (bindAddr, bindPort));
+
+                       EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
+
+                       if (sc.connect (new InetSocketAddress (address, port))) {
+                               // Connection returned immediately. Can happen with localhost connections.
+                               // WARNING, this code is untested due to lack of available test conditions.
+                               // Ought to be be able to come here from a localhost connection, but that
+                               // doesn't happen on Linux. (Maybe on FreeBSD?)
+                               // The reason for not handling this until we can test it is that we
+                               // really need to return from this function WITHOUT triggering any EM events.
+                               // That's because until the user code has seen the signature we generated here,
+                               // it won't be able to properly dispatch them. The C++ EM deals with this
+                               // by setting pending mode as a flag in ALL eventable descriptors and making
+                               // the descriptor select for writable. Then, it can send UNBOUND and
+                               // CONNECTION_COMPLETED on the next pass through the loop, because writable will
+                               // fire.
+                               throw new RuntimeException ("immediate-connect unimplemented");
+                       }
+                       else {
+                               ec.setConnectPending();
+                               Connections.put (b, ec);
+                               NewConnections.add (b);
+                       }
+               } catch (IOException e) {
+                       // Can theoretically come here if a connect failure can be determined immediately.
+                       // I don't know how to make that happen for testing purposes.
+                       throw new RuntimeException ("immediate-connect unimplemented: " + e.toString());
+               }
+               return b;
+       }
+
+       public void closeConnection (long sig, boolean afterWriting) {
+               EventableChannel ec = Connections.get(sig);
+               if (ec != null)
+                       if (ec.scheduleClose (afterWriting))
+                               UnboundConnections.add (sig);
+       }
+       
+       long createBinding() {
+               return ++BindingIndex;
+       }
+
+       public void signalLoopbreak() {
+               loopBreaker.set(true);
+               if (mySelector != null)
+                       mySelector.wakeup();
+       }
+
+       public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException {
+               Connections.get(sig).startTls();
+       }
+
+       public void setTimerQuantum (int mills) {
+               if (mills < 5 || mills > 2500)
+                       throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
+               timerQuantum = mills;
+       }
+
+       public Object[] getPeerName (long sig) {
+               return Connections.get(sig).getPeerName();
+       }
+
+       public long attachChannel (SocketChannel sc, boolean watch_mode) {
+               long b = createBinding();
+
+               EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
+
+               ec.setAttached();
+               if (watch_mode)
+                       ec.setWatchOnly();
+
+               Connections.put (b, ec);
+               NewConnections.add (b);
+
+               return b;
+       }
+
+       public SocketChannel detachChannel (long sig) {
+               EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig);
+               if (ec != null) {
+                       UnboundConnections.add (sig);
+                       return ec.getChannel();
+               } else {
+                       return null;
+               }
+       }
+
+       public void setNotifyReadable (long sig, boolean mode) {
+               ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode);
+       }
+
+       public void setNotifyWritable (long sig, boolean mode) {
+               ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode);
+       }
+
+       public boolean isNotifyReadable (long sig) {
+               return Connections.get(sig).isNotifyReadable();
+       }
+
+       public boolean isNotifyWritable (long sig) {
+               return Connections.get(sig).isNotifyWritable();
+       }
+
+       public int getConnectionCount() {
+         return Connections.size() + Acceptors.size();
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactorException.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EmReactorException.java
new file mode 100644 (file)
index 0000000..491a8e0
--- /dev/null
@@ -0,0 +1,40 @@
+/**\r
+ * $Id$\r
+ * \r
+ * Author:: Francis Cianfrocca (gmail: blackhedd)\r
+ * Homepage::  http://rubyeventmachine.com\r
+ * Date:: 15 Jul 2007\r
+ * \r
+ * See EventMachine and EventMachine::Connection for documentation and\r
+ * usage examples.\r
+ * \r
+ *\r
+ *----------------------------------------------------------------------------\r
+ *\r
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+ * Gmail: blackhedd\r
+ * \r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of either: 1) the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2 of the\r
+ * License, or (at your option) any later version; or 2) Ruby's License.\r
+ * \r
+ * See the file COPYING for complete licensing information.\r
+ *\r
+ *---------------------------------------------------------------------------\r
+ *\r
+ * \r
+ */\r
+\r
+package com.rubyeventmachine;\r
+\r
+/**\r
+ * @author francis\r
+ *\r
+ */\r
+public class EmReactorException extends Exception {\r
+       static final long serialVersionUID = 0;\r
+       public EmReactorException (String msg) {\r
+               super (msg);\r
+       }\r
+}\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableChannel.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableChannel.java
new file mode 100644 (file)
index 0000000..c584719
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine;
+
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+
+public interface EventableChannel {
+       
+       public void scheduleOutboundData (ByteBuffer bb);
+       
+       public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort);
+       
+       public boolean scheduleClose (boolean afterWriting);
+       
+       public void startTls();
+       
+       public long getBinding();
+       
+       public void readInboundData (ByteBuffer dst) throws IOException;
+       
+       public void register() throws ClosedChannelException;
+
+       /**
+        * This is called by the reactor after it finishes running.
+        * The idea is to free network resources.
+        */
+       public void close();
+       
+       public boolean writeOutboundData() throws IOException;
+
+       public void setCommInactivityTimeout (long seconds);
+
+       public Object[] getPeerName();
+
+       public boolean isWatchOnly();
+
+       public boolean isNotifyReadable();
+       public boolean isNotifyWritable();
+
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableDatagramChannel.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableDatagramChannel.java
new file mode 100644 (file)
index 0000000..941b712
--- /dev/null
@@ -0,0 +1,189 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.DatagramChannel;
+import java.util.LinkedList;
+import java.io.*;
+import java.net.*;
+
+public class EventableDatagramChannel implements EventableChannel {
+       
+       class Packet {
+               public ByteBuffer bb;
+               public SocketAddress recipient;
+               public Packet (ByteBuffer _bb, SocketAddress _recipient) {
+                       bb = _bb;
+                       recipient = _recipient;
+               }
+       }
+       
+       DatagramChannel channel;
+       long binding;
+       Selector selector;
+       boolean bCloseScheduled;
+       LinkedList<Packet> outboundQ;
+       SocketAddress returnAddress;
+       
+
+       public EventableDatagramChannel (DatagramChannel dc, long _binding, Selector sel) throws ClosedChannelException {
+               channel = dc;
+               binding = _binding;
+               selector = sel;
+               bCloseScheduled = false;
+               outboundQ = new LinkedList<Packet>();
+               
+               dc.register(selector, SelectionKey.OP_READ, this);
+       }
+
+       public void scheduleOutboundData (ByteBuffer bb) {
+               try {
+                       if ((!bCloseScheduled) && (bb.remaining() > 0)) {
+                               outboundQ.addLast(new Packet(bb, returnAddress));
+                               channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
+                       }
+               } catch (ClosedChannelException e) {
+                       throw new RuntimeException ("no outbound data");                        
+               }
+       }
+       
+       public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
+               try {
+                       if ((!bCloseScheduled) && (bb.remaining() > 0)) {
+                               outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort)));
+                               channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
+                       }
+               } catch (ClosedChannelException e) {
+                       throw new RuntimeException ("no outbound data");                        
+               }
+       }
+       
+       public boolean scheduleClose (boolean afterWriting) {
+               System.out.println ("NOT SCHEDULING CLOSE ON DATAGRAM");
+               return false;
+       }
+       
+       public void startTls() {
+               throw new RuntimeException ("TLS is unimplemented on this Channel");
+       }
+       
+       public long getBinding() {
+               return binding;
+       }
+
+       public void register() throws ClosedChannelException {
+               // TODO
+       }
+
+       /**
+        * Terminate with extreme prejudice. Don't assume there will be another pass through
+        * the reactor core.
+        */
+       public void close() {
+               try {
+                       channel.close();
+               } catch (IOException e) {
+               }
+       }
+       
+       public void readInboundData (ByteBuffer dst) {
+               returnAddress = null;
+               try {
+                       // If there is no datagram available (we're nonblocking after all),
+                       // then channel.receive returns null.
+                       returnAddress = channel.receive(dst);
+               } catch (IOException e) {
+                       // probably a no-op. The caller will see the empty (or even partial) buffer
+                       // and presumably do the right thing.
+               }
+       }
+       
+       public boolean writeOutboundData() {
+               while (!outboundQ.isEmpty()) {
+                       Packet p = outboundQ.getFirst();
+                       int written = 0;
+                       try {
+                               // With a datagram socket, it's ok to send an empty buffer.
+                               written = channel.send(p.bb, p.recipient);
+                       }
+                       catch (IOException e) {
+                               return false;
+                       }
+
+                       /* Did we consume the whole outbound buffer? If yes, pop it off and
+                        * keep looping. If no, the outbound network buffers are full, so break
+                        * out of here. There's a flaw that affects outbound buffers that are intentionally
+                        * empty. We can tell whether they got sent or not. So we assume they were.
+                        * TODO: As implemented, this ALWAYS discards packets if they were at least
+                        * partially written. This matches the behavior of the C++ EM. My judgment
+                        * is that this is less surprising than fragmenting the data and sending multiple
+                        * packets would be. I could be wrong, so this is subject to change.
+                        */
+
+                       if ((written > 0) || (p.bb.remaining() == 0))
+                               outboundQ.removeFirst();
+                       else
+                               break;
+               }
+
+               if (outboundQ.isEmpty()) {
+                       try {
+                               channel.register(selector, SelectionKey.OP_READ, this);
+                       } catch (ClosedChannelException e) {}
+               }
+               
+               // ALWAYS drain the outbound queue before triggering a connection close.
+               // If anyone wants to close immediately, they're responsible for clearing
+               // the outbound queue.
+               return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
+       }
+
+       public void setCommInactivityTimeout (long seconds) {
+               // TODO
+               System.out.println ("DATAGRAM: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
+       }
+
+       public Object[] getPeerName () {
+               if (returnAddress != null) {
+                       InetSocketAddress inetAddr = (InetSocketAddress) returnAddress;
+                       return new Object[]{ inetAddr.getPort(), inetAddr.getHostName() };
+               } else {
+                       return null;
+               }
+       }
+
+       public boolean isWatchOnly() { return false; }
+       public boolean isNotifyReadable() { return false; }
+       public boolean isNotifyWritable() { return false; }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableSocketChannel.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/EventableSocketChannel.java
new file mode 100644 (file)
index 0000000..18f7504
--- /dev/null
@@ -0,0 +1,364 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+/**
+ * 
+ */
+package com.rubyeventmachine;
+
+/**
+ * @author francis
+ *
+ */
+
+import java.nio.channels.*;
+import java.nio.*;
+import java.util.*;
+import java.io.*;
+import java.net.Socket;
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.lang.reflect.Field;
+
+import java.security.*;
+
+public class EventableSocketChannel implements EventableChannel {
+       Selector selector;
+       SelectionKey channelKey;
+       SocketChannel channel;
+
+       long binding;
+       LinkedList<ByteBuffer> outboundQ;
+
+       boolean bCloseScheduled;
+       boolean bConnectPending;
+       boolean bWatchOnly;
+       boolean bAttached;
+       boolean bNotifyReadable;
+       boolean bNotifyWritable;
+       
+       SSLEngine sslEngine;
+       SSLContext sslContext;
+
+       public EventableSocketChannel (SocketChannel sc, long _binding, Selector sel) {
+               channel = sc;
+               binding = _binding;
+               selector = sel;
+               bCloseScheduled = false;
+               bConnectPending = false;
+               bWatchOnly = false;
+               bAttached = false;
+               bNotifyReadable = false;
+               bNotifyWritable = false;
+               outboundQ = new LinkedList<ByteBuffer>();
+       }
+       
+       public long getBinding() {
+               return binding;
+       }
+
+       public SocketChannel getChannel() {
+               return channel;
+       }
+
+       public void register() throws ClosedChannelException {
+               if (channelKey == null) {
+                       int events = currentEvents();
+                       channelKey = channel.register(selector, events, this);
+               }
+       }
+
+       /**
+        * Terminate with extreme prejudice. Don't assume there will be another pass through
+        * the reactor core.
+        */
+       public void close() {
+               if (channelKey != null) {
+                       channelKey.cancel();
+                       channelKey = null;
+               }
+
+               if (bAttached) {
+                       // attached channels are copies, so reset the file descriptor to prevent java from close()ing it
+                       Field f;
+                       FileDescriptor fd;
+
+                       try {
+                               /* do _NOT_ clobber fdVal here, it will break epoll/kqueue on jdk6!
+                                * channelKey.cancel() above does not occur until the next call to select
+                                * and if fdVal is gone, we will continue to get events for this fd.
+                                *
+                                * instead, remove fdVal in cleanup(), which is processed via DetachedConnections,
+                                * after UnboundConnections but before NewConnections.
+                                */
+
+                               f = channel.getClass().getDeclaredField("fd");
+                               f.setAccessible(true);
+                               fd = (FileDescriptor) f.get(channel);
+
+                               f = fd.getClass().getDeclaredField("fd");
+                               f.setAccessible(true);
+                               f.set(fd, -1);
+                       } catch (java.lang.NoSuchFieldException e) {
+                               e.printStackTrace();
+                       } catch (java.lang.IllegalAccessException e) {
+                               e.printStackTrace();
+                       }
+
+                       return;
+               }
+
+               try {
+                       channel.close();
+               } catch (IOException e) {
+               }
+       }
+
+       public void cleanup() {
+               if (bAttached) {
+                       Field f;
+                       try {
+                               f = channel.getClass().getDeclaredField("fdVal");
+                               f.setAccessible(true);
+                               f.set(channel, -1);
+                       } catch (java.lang.NoSuchFieldException e) {
+                               e.printStackTrace();
+                       } catch (java.lang.IllegalAccessException e) {
+                               e.printStackTrace();
+                       }
+               }
+
+               channel = null;
+       }
+       
+       public void scheduleOutboundData (ByteBuffer bb) {
+               if (!bCloseScheduled && bb.remaining() > 0) {
+                       if (sslEngine != null) {
+                               try {
+                                       ByteBuffer b = ByteBuffer.allocate(32*1024); // TODO, preallocate this buffer.
+                                       sslEngine.wrap(bb, b);
+                                       b.flip();
+                                       outboundQ.addLast(b);
+                               } catch (SSLException e) {
+                                       throw new RuntimeException ("ssl error");
+                               }
+                       }
+                       else {
+                               outboundQ.addLast(bb);
+                       }
+
+                       updateEvents();
+               }
+       }
+       
+       public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
+               throw new RuntimeException ("datagram sends not supported on this channel");
+       }
+       
+       /**
+        * Called by the reactor when we have selected readable.
+        */
+       public void readInboundData (ByteBuffer bb) throws IOException {
+               if (channel.read(bb) == -1)
+                       throw new IOException ("eof");
+       }
+
+       /**
+        * Called by the reactor when we have selected writable.
+        * Return false to indicate an error that should cause the connection to close.
+        * TODO, VERY IMPORTANT: we're here because we selected writable, but it's always
+        * possible to become unwritable between the poll and when we get here. The way
+        * this code is written, we're depending on a nonblocking write NOT TO CONSUME
+        * the whole outbound buffer in this case, rather than firing an exception.
+        * We should somehow verify that this is indeed Java's defined behavior.
+        * Also TODO, see if we can use gather I/O rather than one write at a time.
+        * Ought to be a big performance enhancer.
+        * @return
+        */
+       public boolean writeOutboundData() throws IOException {
+               while (!outboundQ.isEmpty()) {
+                       ByteBuffer b = outboundQ.getFirst();
+                       if (b.remaining() > 0)
+                               channel.write(b);
+
+                       // Did we consume the whole outbound buffer? If yes,
+                       // pop it off and keep looping. If no, the outbound network
+                       // buffers are full, so break out of here.
+                       if (b.remaining() == 0)
+                               outboundQ.removeFirst();
+                       else
+                               break;
+               }
+
+               if (outboundQ.isEmpty() && !bCloseScheduled) {
+                       updateEvents();
+               }
+
+               // ALWAYS drain the outbound queue before triggering a connection close.
+               // If anyone wants to close immediately, they're responsible for clearing
+               // the outbound queue.
+               return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
+       }
+       
+       public void setConnectPending() {
+               bConnectPending = true;
+               updateEvents();
+       }
+       
+       /**
+        * Called by the reactor when we have selected connectable.
+        * Return false to indicate an error that should cause the connection to close.
+        */
+       public boolean finishConnecting() throws IOException {
+               channel.finishConnect();
+
+               bConnectPending = false;
+               updateEvents();
+               return true;
+       }
+       
+       public boolean scheduleClose (boolean afterWriting) {
+               // TODO: What the hell happens here if bConnectPending is set?
+               if (!afterWriting)
+                       outboundQ.clear();
+
+               if (outboundQ.isEmpty())
+                       return true;
+               else {
+                       updateEvents();
+                       bCloseScheduled = true;
+                       return false;
+               }
+       }
+
+       public void startTls() {
+               if (sslEngine == null) {
+                       try {
+                               sslContext = SSLContext.getInstance("TLS");
+                               sslContext.init(null, null, null); // TODO, fill in the parameters.
+                               sslEngine = sslContext.createSSLEngine(); // TODO, should use the parameterized version, to get Kerb stuff and session re-use.
+                               sslEngine.setUseClientMode(false);
+                       } catch (NoSuchAlgorithmException e) {
+                               throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.                           
+                       } catch (KeyManagementException e) {
+                               throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.                           
+                       }
+               }
+               System.out.println ("Starting TLS");
+       }
+       
+       public ByteBuffer dispatchInboundData (ByteBuffer bb) throws SSLException {
+               if (sslEngine != null) {
+                       if (true) throw new RuntimeException ("TLS currently unimplemented");
+                       System.setProperty("javax.net.debug", "all");
+                       ByteBuffer w = ByteBuffer.allocate(32*1024); // TODO, WRONG, preallocate this buffer.
+                       SSLEngineResult res = sslEngine.unwrap(bb, w);
+                       if (res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+                               Runnable r;
+                               while ((r = sslEngine.getDelegatedTask()) != null) {
+                                       r.run();
+                               }
+                       }
+                       System.out.println (bb);
+                       w.flip();
+                       return w;
+               }
+               else
+                       return bb;
+       }
+
+       public void setCommInactivityTimeout (long seconds) {
+               // TODO
+               System.out.println ("SOCKET: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
+       }
+
+       public Object[] getPeerName () {
+               Socket sock = channel.socket();
+               return new Object[]{ sock.getPort(), sock.getInetAddress().getHostAddress() };
+       }
+
+       public void setWatchOnly() {
+               bWatchOnly = true;
+               updateEvents();
+       }
+       public boolean isWatchOnly() { return bWatchOnly; }
+
+       public void setAttached() {
+               bAttached = true;
+       }
+       public boolean isAttached() { return bAttached; }
+
+       public void setNotifyReadable (boolean mode) {
+               bNotifyReadable = mode;
+               updateEvents();
+       }
+       public boolean isNotifyReadable() { return bNotifyReadable; }
+
+       public void setNotifyWritable (boolean mode) {
+               bNotifyWritable = mode;
+               updateEvents();
+       }
+       public boolean isNotifyWritable() { return bNotifyWritable; }
+
+       private void updateEvents() {
+               if (channelKey == null)
+                       return;
+
+               int events = currentEvents();
+
+               if (channelKey.interestOps() != events) {
+                       channelKey.interestOps(events);
+               }
+       }
+
+       private int currentEvents() {
+               int events = 0;
+
+               if (bWatchOnly)
+               {
+                       if (bNotifyReadable)
+                               events |= SelectionKey.OP_READ;
+
+                       if (bNotifyWritable)
+                               events |= SelectionKey.OP_WRITE;
+               }
+               else
+               {
+                       if (bConnectPending)
+                               events |= SelectionKey.OP_CONNECT;
+                       else {
+                               events |= SelectionKey.OP_READ;
+
+                               if (!outboundQ.isEmpty())
+                                       events |= SelectionKey.OP_WRITE;
+                       }
+               }
+
+               return events;
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Application.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Application.java
new file mode 100644 (file)
index 0000000..bedbfb4
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+/**
+ * 
+ */
+package com.rubyeventmachine.application;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import java.net.SocketAddress;
+
+import com.rubyeventmachine.*;
+
+/**
+ * @author francis
+ *
+ */
+public class Application {
+       
+       
+       public class Reactor extends EmReactor {
+
+               private Application application;
+               private TreeMap<Long, Timer> timers;
+               private TreeMap<Long, Connection> connections;
+               private TreeMap<Long, ConnectionFactory> acceptors;
+               /**
+                * 
+                */
+               public Reactor (Application app) {
+                       application = app;
+                       timers = new TreeMap<Long, Timer>();
+                       connections = new TreeMap<Long, Connection>();
+                       acceptors = new TreeMap<Long, ConnectionFactory>();
+               }
+
+
+               public void eventCallback (long sig, int eventType, ByteBuffer data, long data2) {
+                       if (eventType == EM_TIMER_FIRED) {
+                               Timer r = timers.remove(data2);
+                               if (r != null)
+                                       r._fire();
+                               else
+                                       throw new RuntimeException ("unable to run unknown timer");
+                       }
+                       else if (eventType == EM_CONNECTION_COMPLETED) {
+                               Connection c = connections.get(sig);
+                               if (c != null) {
+                                       c.connectionCompleted();
+                               }
+                               else
+                                       throw new RuntimeException ("connection completed to unknown object");
+
+                       }
+                       else if (eventType == EM_CONNECTION_UNBOUND) {
+                               Connection c = connections.get(sig);
+                               if (c != null) {
+                                       c.unbind();
+                               }
+                               else
+                                       throw new RuntimeException ("unbind received on unknown object");
+                       }
+                       else if (eventType == EM_CONNECTION_ACCEPTED) {
+                               ConnectionFactory f = acceptors.get(sig);
+                               if (f != null) {
+                                       Connection c = f.connection();
+                                       c.signature = data2;
+                                       c.application = application;
+                                       connections.put(c.signature, c);
+                                       c.postInit();
+                                       //System.out.println (sig+"..."+new String(data.array()));
+                               }
+                               else
+                                       throw new RuntimeException ("received connection on unknown acceptor");
+                       }
+                       else if (eventType == EM_CONNECTION_READ) {
+                               Connection c = connections.get(sig);
+                               if (c != null) {
+                                       c.receiveData(data);
+                               }
+                               else throw new RuntimeException ("received data on unknown object");
+                       }
+                       else {
+                               System.out.println ("unknown event type: " + eventType);
+                       }
+               }
+       }
+
+
+       Reactor reactor;
+       
+       public Application() {
+               reactor = new Reactor (this);
+       }
+       public void addTimer (double seconds, Timer t) {
+               t.application = this;
+               t.interval = seconds;
+               long s = reactor.installOneshotTimer ((int)(seconds * 1000));
+               reactor.timers.put(s, t);
+               
+       }
+
+       public void bindConnect (String bindAddr, int bindPort, String host, int port, Connection c) {
+               long s = reactor.connectTcpServer(bindAddr, bindPort, host, port);
+               c.application = this;
+               c.signature = s;
+               reactor.connections.put(s, c);
+               c.postInit();
+       }
+
+       public void connect (String host, int port, Connection c) {
+               bindConnect(null, 0, host, port, c);
+       }
+       
+       public void startServer (SocketAddress sa, ConnectionFactory f) throws EmReactorException {
+               long s = reactor.startTcpServer(sa);
+               reactor.acceptors.put(s, f);
+       }
+       
+       public void stop() {
+               reactor.stop();
+       }
+       public void run() {
+               reactor.run();
+       }
+       public void run (final Runnable r) {
+               addTimer(0, new Timer() {
+                       public void fire() {
+                               r.run();
+                       }
+               });
+               run();
+       }
+       
+       public void sendData (long sig, ByteBuffer bb) {
+               try {
+                       reactor.sendData(sig, bb);
+               } catch (IOException e) {}
+       }
+       
+       public void sendDatagram (long sig, ByteBuffer bb, InetSocketAddress target) {
+               reactor.sendDatagram(sig, bb, target.getHostName(), target.getPort());
+       }
+       
+       public void closeConnection (long sig, boolean afterWriting) {
+               reactor.closeConnection(sig, afterWriting);
+       }
+       
+       public void openDatagramSocket (Connection c) {
+               openDatagramSocket (new InetSocketAddress ("0.0.0.0", 0), c);
+       }
+       public void openDatagramSocket (InetSocketAddress addr, Connection c) {
+               try {
+                       long s = reactor.openUdpSocket(addr);
+                       c.application = this;
+                       c.signature = s;
+                       reactor.connections.put(s, c);
+                       c.postInit();
+               } catch (ClosedChannelException e) {
+               } catch (IOException e) {
+                       System.out.println ("Bad Datagram socket "+e+" "+addr);
+                       /* TODO, can't catch this here, because it can happen on a bad address */
+               }
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Connection.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Connection.java
new file mode 100644 (file)
index 0000000..68fc553
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+
+package com.rubyeventmachine.application;
+
+//import java.io.*;
+import java.nio.*;
+import java.net.*;
+//import java.nio.channels.*;
+
+public class Connection {
+       
+       public Application application;
+       public long signature;
+       
+       public void postInit() {}
+       public void connectionCompleted() {}
+       public void unbind() {}
+       public void receiveData (ByteBuffer bytebuffer) {}
+       
+       
+       /**
+        * Called by user code.
+        * @param bytebuffer
+        */
+       public void sendData (ByteBuffer b) {
+               application.sendData(signature, b);
+       }
+       
+       /**
+        * This is called by user code.
+        * TODO: don't expose the exception here.
+        */
+       public void close() {
+               application.closeConnection(signature, false);
+       }
+       /**
+        * This is called by user code/
+        */
+       public void closeAfterWriting() {
+               application.closeConnection(signature, true);
+       }
+       
+       public void sendDatagram (ByteBuffer bb, InetSocketAddress addr) {
+               application.sendDatagram (signature, bb, addr);
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/ConnectionFactory.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/ConnectionFactory.java
new file mode 100644 (file)
index 0000000..868250d
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+
+package com.rubyeventmachine.application;
+
+//import com.rubyeventmachine.*;
+
+public interface ConnectionFactory {
+       public Connection connection();
+}
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java
new file mode 100644 (file)
index 0000000..ebe864b
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+
+package com.rubyeventmachine.application;
+
+import com.rubyeventmachine.application.ConnectionFactory;
+
+public class DefaultConnectionFactory implements ConnectionFactory {
+
+       /**
+        * Convenience class. Its connection() method returns an instance of class
+        * Connection, which is usually overridden. This class is probably most
+        * useful for unit testing.
+        */
+       public Connection connection() {
+               return new Connection();
+       }
+
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/PeriodicTimer.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/PeriodicTimer.java
new file mode 100644 (file)
index 0000000..5e39ed2
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine.application;
+
+public class PeriodicTimer extends Timer {
+
+       public void _fire() {
+               fire();
+               application.addTimer(interval, this);
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Timer.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/application/Timer.java
new file mode 100644 (file)
index 0000000..a7fef7d
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine.application;
+
+public class Timer {
+       /**
+        * User code is expected to call a method on a controlling Application,
+        * which will fill in this field so subsequent user code can access it.
+        */
+       public Application application;
+       public double interval;
+       
+       /**
+        * The reactor calls here, and it may be overridden in subclasses.
+        * User code should never call this method.
+        */
+       public void _fire() {
+               fire();
+       }
+
+       /**
+        * User code is expected to override this method.
+        */
+       public void fire() {
+       }
+
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ApplicationTest.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ApplicationTest.java
new file mode 100644 (file)
index 0000000..ee85ea5
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+
+package com.rubyeventmachine.tests;
+
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.Assert;
+import java.net.*;
+import java.io.*;
+import java.nio.*;
+
+import com.rubyeventmachine.*;
+import com.rubyeventmachine.application.*;
+
+public class ApplicationTest {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void testRunnableArgument() {
+               final Application a = new Application();
+               a.run (new Runnable() {
+                       public void run() {
+                               a.stop();
+                       }
+               });
+       }
+       
+
+       
+       class F implements ConnectionFactory {
+               public Connection connection() {
+                       return new Connection() {
+                               public void receiveData (ByteBuffer bb) {
+                                       application.stop();
+                               }
+                       };
+               }
+               
+       };
+       
+       @Test
+       public void testTcpServer() throws EmReactorException {
+               final Application a = new Application();
+               final SocketAddress saddr = new InetSocketAddress ("127.0.0.1", 9008);
+               a.run (new Runnable() {
+                       public void run() {
+                               try {
+                                       a.startServer (saddr, new F());
+                               } catch (EmReactorException e) { Assert.fail(); }
+                               new Thread() {
+                                       public void run() {
+                                               try {
+                                                       Socket s = new Socket ("127.0.0.1", 9008);
+                                                       s.getOutputStream().write(new String ("boo").getBytes());
+                                               } catch (UnknownHostException e) {
+                                               } catch (IOException e) {}
+                                       }
+                               }.start();
+                       }
+               });
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ConnectTest.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/ConnectTest.java
new file mode 100644 (file)
index 0000000..c5ea088
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine.tests;
+
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import java.net.*;
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+
+import com.rubyeventmachine.*;
+import com.rubyeventmachine.application.*;
+
+public class ConnectTest {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public final void test1() throws IOException, ClosedChannelException {
+               Application a = new Application();
+               a.addTimer(0, new Timer() {
+                       public void fire() {
+                               application.connect("www.bayshorenetworks.com", 80, new Connection() {
+                                       public void connectionCompleted() {
+                                               close();
+                                       }
+                                       public void unbind() {
+                                               application.stop();
+                                       }
+                               });
+                       }
+               });
+               a.run();
+       }
+       
+       @Test
+       public final void test2() throws IOException {
+               class Bays extends Connection {
+                       public void connectionCompleted() {
+                               sendData (ByteBuffer.wrap( new String ("GET / HTTP/1.1\r\nHost: _\r\n\r\n").getBytes()));
+                       }
+                       public void receiveData (ByteBuffer b) {
+                               System.out.println (new String(b.array()));
+                               application.stop();
+                       }
+               };
+               
+               Application a = new Application();
+               a.addTimer(0, new Timer() {
+                       public void fire() {
+                               application.connect("www.bayshorenetworks.com", 80, new Bays());
+                       }
+               });
+               a.run();
+       }
+
+       public final void testBindConnect() throws IOException {
+               class Server extends Connection {
+                       public void postInit() {
+                               // TODO: get peername here and check if the port is 33333
+                               // doesnt seem like peername is impl yet?
+                               System.out.println("post init!");
+                       }
+               };
+
+               Application a = new Application();
+               a.addTimer(0, new Timer() {
+                       public void fire() {
+                               application.startServer(new InetSocketAddress("localhost", 20000), new DefaultConnectionFactory());
+                       }
+               });
+               a.addTimer(500, new Timer() {
+                       public void fire() {
+                               application.bindConnect("localhost", 33333, "localhost", 20000, new Connection());
+                       }
+               });
+
+               a.run();
+       }
+
+       class C1 extends Connection {
+               Application application;
+               public C1 (Application a) {
+                       application = a;
+               }
+               public void postInit() {
+                       application.stop();
+               }
+       }
+       @Test
+       public final void test3() {
+               final Application a = new Application();
+               a.run (new Runnable() {
+                       public void run() {
+                               a.connect("www.bayshorenetworks.com", 80, new C1(a));
+                       }
+               });
+       }
+       
+       
+
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/EMTest.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/EMTest.java
new file mode 100644 (file)
index 0000000..859f805
--- /dev/null
@@ -0,0 +1,80 @@
+/**\r
+ * $Id$\r
+ * \r
+ * Author:: Francis Cianfrocca (gmail: blackhedd)\r
+ * Homepage::  http://rubyeventmachine.com\r
+ * Date:: 15 Jul 2007\r
+ * \r
+ * See EventMachine and EventMachine::Connection for documentation and\r
+ * usage examples.\r
+ * \r
+ *\r
+ *----------------------------------------------------------------------------\r
+ *\r
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+ * Gmail: blackhedd\r
+ * \r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of either: 1) the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2 of the\r
+ * License, or (at your option) any later version; or 2) Ruby's License.\r
+ * \r
+ * See the file COPYING for complete licensing information.\r
+ *\r
+ *---------------------------------------------------------------------------\r
+ *\r
+ * \r
+ */\r
+\r
+\r
+package com.rubyeventmachine.tests;\r
+\r
+//import static org.junit.Assert.*;\r
+\r
+import org.junit.After;\r
+import org.junit.AfterClass;\r
+import org.junit.Before;\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import com.rubyeventmachine.EmReactor;\r
+\r
+import java.io.*;\r
+import java.nio.*;\r
+\r
+\r
+public class EMTest {\r
+\r
+       class ShortTimer extends EmReactor {\r
+               public void eventCallback (String sig, int eventCode, ByteBuffer data) {\r
+                       System.out.println ("Short Callback "+sig+" "+eventCode+" "+data);\r
+                       this.stop();\r
+               }\r
+       }\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+       }\r
+\r
+       @AfterClass\r
+       public static void tearDownAfterClass() throws Exception {\r
+       }\r
+\r
+       @Before\r
+       public void setUp() throws Exception {\r
+       }\r
+\r
+       @After\r
+       public void tearDown() throws Exception {\r
+       }\r
\r
+\r
+       @Test\r
+       public final void testOneShort() throws IOException {\r
+               EmReactor em = new ShortTimer();\r
+               em.installOneshotTimer(1050);\r
+               em.run();\r
+       }\r
+       \r
+\r
+}\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestDatagrams.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestDatagrams.java
new file mode 100644 (file)
index 0000000..1abc954
--- /dev/null
@@ -0,0 +1,53 @@
+package com.rubyeventmachine.tests;
+
+import com.rubyeventmachine.application.*;
+import java.net.*;
+import java.nio.*;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class TestDatagrams {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       class A extends Connection {
+               public void receiveData (ByteBuffer bb) {
+                       application.stop();
+               }
+       }
+       class B extends Connection {
+               public void postInit() {
+                       this.sendDatagram(ByteBuffer.wrap(new String("ABC").getBytes()), new InetSocketAddress ("127.0.0.1", 9550));
+               }
+               
+       }
+       @Test
+       public final void testA() {
+               final Application a = new Application();
+               a.run (new Runnable() {
+                       public void run() {
+                               a.openDatagramSocket( new InetSocketAddress ("0.0.0.0", 9550), new A() );
+                               a.openDatagramSocket( new B() );
+                       }
+               });
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestServers.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestServers.java
new file mode 100644 (file)
index 0000000..cb65637
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine.tests;
+
+
+import com.rubyeventmachine.*;
+import com.rubyeventmachine.application.*;
+import java.net.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class TestServers {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       
+       @Test
+       public void testBadServerAddress() {
+               final Application a = new Application();
+               a.run (new Runnable() {
+                       public void run() {
+                               try {
+                                       a.startServer(new InetSocketAddress ("100.100.100.100", 100), new DefaultConnectionFactory());
+                                       Assert.fail ("was supposed to throw a reactor exception");
+                               } catch (EmReactorException e) {}
+                               a.stop();
+                       }
+               });
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestTimers.java b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/java/src/com/rubyeventmachine/tests/TestTimers.java
new file mode 100644 (file)
index 0000000..1057100
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * $Id$
+ * 
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage::  http://rubyeventmachine.com
+ * Date:: 15 Jul 2007
+ * 
+ * See EventMachine and EventMachine::Connection for documentation and
+ * usage examples.
+ * 
+ *
+ *----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+ * Gmail: blackhedd
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of either: 1) the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version; or 2) Ruby's License.
+ * 
+ * See the file COPYING for complete licensing information.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 
+ */
+
+
+package com.rubyeventmachine.tests;
+
+import com.rubyeventmachine.*;
+import com.rubyeventmachine.application.*;
+import java.io.*;
+
+import org.junit.Assert;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class TestTimers {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       
+       @Test
+       public final void test2() throws IOException {
+               Application a = new Application();
+               a.addTimer(0, new Timer() {
+                       public void fire() {
+                               application.stop();
+                       }
+               });
+               a.run();
+               Assert.assertEquals (1, 1); // just to make sure the reactor halts.
+       }
+       
+       @Test
+       public final void test3() throws IOException {
+               Application a = new Application();
+               a.addTimer (0.1, new PeriodicTimer() {
+                       int n = 0;
+                       public void fire() {
+                               n++;
+                               if (n == 5)
+                                       application.stop();
+                       }
+               });
+               a.run();
+               Assert.assertEquals(1, 1);
+       }
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/buftok.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/buftok.rb
new file mode 100644 (file)
index 0000000..585c2b0
--- /dev/null
@@ -0,0 +1,138 @@
+# BufferedTokenizer - Statefully split input data by a specifiable token\r
+#\r
+# Authors:: Tony Arcieri, Martin Emde\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Tony Arcieri and Martin Emde\r
+# \r
+# Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+# (C)2006 Tony Arcieri, Martin Emde\r
+# Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)\r
+\r
+# BufferedTokenizer takes a delimiter upon instantiation, or acts line-based\r
+# by default.  It allows input to be spoon-fed from some outside source which\r
+# receives arbitrary length datagrams which may-or-may-not contain the token\r
+# by which entities are delimited.\r
+#\r
+# Commonly used to parse lines out of incoming data:\r
+#\r
+#  module LineBufferedConnection\r
+#    def receive_data(data)\r
+#      (@buffer ||= BufferedTokenizer.new).extract(data).each do |line|\r
+#        receive_line(line)\r
+#      end\r
+#    end\r
+#  end\r
+\r
+class BufferedTokenizer\r
+  # New BufferedTokenizers will operate on lines delimited by "\n" by default\r
+  # or allow you to specify any delimiter token you so choose, which will then\r
+  # be used by String#split to tokenize the input data\r
+  def initialize(delimiter = "\n", size_limit = nil)\r
+    # Store the specified delimiter\r
+    @delimiter = delimiter\r
+\r
+    # Store the specified size limitation\r
+    @size_limit = size_limit\r
+\r
+    # The input buffer is stored as an array.  This is by far the most efficient\r
+    # approach given language constraints (in C a linked list would be a more\r
+    # appropriate data structure).  Segments of input data are stored in a list\r
+    # which is only joined when a token is reached, substantially reducing the\r
+    # number of objects required for the operation.\r
+    @input = []\r
+\r
+    # Size of the input buffer\r
+    @input_size = 0\r
+  end\r
+\r
+  # Extract takes an arbitrary string of input data and returns an array of\r
+  # tokenized entities, provided there were any available to extract.  This\r
+  # makes for easy processing of datagrams using a pattern like:\r
+  #\r
+  #   tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...\r
+  def extract(data)\r
+    # Extract token-delimited entities from the input string with the split command.\r
+    # There's a bit of craftiness here with the -1 parameter.  Normally split would\r
+    # behave no differently regardless of if the token lies at the very end of the \r
+    # input buffer or not (i.e. a literal edge case)  Specifying -1 forces split to\r
+    # return "" in this case, meaning that the last entry in the list represents a\r
+    # new segment of data where the token has not been encountered\r
+    entities = data.split @delimiter, -1\r
+\r
+    # Check to see if the buffer has exceeded capacity, if we're imposing a limit\r
+    if @size_limit\r
+      raise 'input buffer full' if @input_size + entities.first.size > @size_limit\r
+      @input_size += entities.first.size\r
+    end\r
+    \r
+    # Move the first entry in the resulting array into the input buffer.  It represents\r
+    # the last segment of a token-delimited entity unless it's the only entry in the list.\r
+    @input << entities.shift\r
+\r
+    # If the resulting array from the split is empty, the token was not encountered\r
+    # (not even at the end of the buffer).  Since we've encountered no token-delimited\r
+    # entities this go-around, return an empty array.\r
+    return [] if entities.empty?\r
+\r
+    # At this point, we've hit a token, or potentially multiple tokens.  Now we can bring\r
+    # together all the data we've buffered from earlier calls without hitting a token,\r
+    # and add it to our list of discovered entities.\r
+    entities.unshift @input.join\r
+\r
+=begin\r
+    # Note added by FC, 10Jul07. This paragraph contains a regression. It breaks\r
+    # empty tokens. Think of the empty line that delimits an HTTP header. It will have\r
+    # two "\n" delimiters in a row, and this code mishandles the resulting empty token.\r
+    # It someone figures out how to fix the problem, we can re-enable this code branch.\r
+    # Multi-character token support.\r
+    # Split any tokens that were incomplete on the last iteration buf complete now.\r
+    entities.map! do |e|\r
+      e.split @delimiter, -1\r
+    end\r
+    # Flatten the resulting array.  This has the side effect of removing the empty\r
+    # entry at the end that was produced by passing -1 to split.  Add it again if\r
+    # necessary.\r
+    if (entities[-1] == [])\r
+      entities.flatten! << []\r
+    else\r
+      entities.flatten!\r
+    end\r
+=end\r
+\r
+    # Now that we've hit a token, joined the input buffer and added it to the entities\r
+    # list, we can go ahead and clear the input buffer.  All of the segments that were\r
+    # stored before the join can now be garbage collected.\r
+    @input.clear\r
+    \r
+    # The last entity in the list is not token delimited, however, thanks to the -1\r
+    # passed to split.  It represents the beginning of a new list of as-yet-untokenized  \r
+    # data, so we add it to the start of the list.\r
+    @input << entities.pop\r
+    \r
+    # Set the new input buffer size, provided we're keeping track\r
+    @input_size = @input.first.size if @size_limit\r
+\r
+    # Now we're left with the list of extracted token-delimited entities we wanted\r
+    # in the first place.  Hooray!\r
+    entities\r
+  end\r
+  \r
+  # Flush the contents of the input buffer, i.e. return the input buffer even though\r
+  # a token has not yet been encountered\r
+  def flush\r
+    buffer = @input.join\r
+    @input.clear\r
+    buffer\r
+  end\r
+\r
+  # Is the buffer empty?\r
+  def empty?\r
+    @input.empty?\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/callback.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/callback.rb
new file mode 100644 (file)
index 0000000..703d365
--- /dev/null
@@ -0,0 +1,26 @@
+module EventMachine\r
+  # Utility method for coercing arguments to an object that responds to #call\r
+  # Accepts an object and a method name to send to, or a block, or an object\r
+  # that responds to call.\r
+  #\r
+  #  cb = EM.Callback{ |msg| puts(msg) }\r
+  #  cb.call('hello world')\r
+  #\r
+  #  cb = EM.Callback(Object, :puts)\r
+  #  cb.call('hello world')\r
+  #\r
+  #  cb = EM.Callback(proc{ |msg| puts(msg) })\r
+  #  cb.call('hello world')\r
+  #\r
+  def self.Callback(object = nil, method = nil, &blk)\r
+    if object && method\r
+      lambda { |*args| object.send method, *args }\r
+    else\r
+      if object.respond_to? :call\r
+        object\r
+      else \r
+        blk || raise(ArgumentError)\r
+      end\r
+    end\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/channel.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/channel.rb
new file mode 100644 (file)
index 0000000..d848cc2
--- /dev/null
@@ -0,0 +1,57 @@
+module EventMachine
+  # Provides a simple interface to push items to a number of subscribers. The
+  # channel will schedule all operations on the main reactor thread for thread
+  # safe reactor operations.
+  #
+  # This provides a convenient way for connections to consume messages from 
+  # long running code in defer, without threading issues.
+  #
+  #  channel = EM::Channel.new
+  #  sid = channel.subscribe{ |msg| p [:got, msg] }
+  #  channel.push('hello world')
+  #  channel.unsubscribe(sid)
+  #
+  # See examples/ex_channel.rb for a detailed example.
+  class Channel
+    # Create a new channel
+    def initialize
+      @subs = {}
+      @uid = 0
+    end
+
+    # Takes any arguments suitable for EM::Callback() and returns a subscriber
+    # id for use when unsubscribing.
+    def subscribe(*a, &b)
+      name = gen_id
+      EM.schedule { @subs[name] = EM::Callback(*a, &b) }
+      name
+    end
+
+    # Removes this subscriber from the list.
+    def unsubscribe(name)
+      EM.schedule { @subs.delete name }
+    end
+
+    # Add items to the channel, which are pushed out to all subscribers.
+    def push(*items)
+      items = items.dup
+      EM.schedule { @subs.values.each { |s| items.each { |i| s.call i } } }
+    end
+    alias << push
+
+    # Receive exactly one message from the channel.
+    def pop(*a, &b)
+      EM.schedule {
+        name = subscribe do |*args|
+          unsubscribe(name)
+          EM::Callback(*a, &b).call(*args)
+        end
+      }
+    end
+
+    private
+    def gen_id # :nodoc:
+      @uid += 1
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/connection.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/connection.rb
new file mode 100644 (file)
index 0000000..6acb69b
--- /dev/null
@@ -0,0 +1,564 @@
+module EventMachine
+  class FileNotFoundException < Exception # :nodoc:
+  end
+
+  # EventMachine::Connection is a class that is instantiated
+  # by EventMachine's processing loop whenever a new connection
+  # is created. (New connections can be either initiated locally
+  # to a remote server or accepted locally from a remote client.)
+  # When a Connection object is instantiated, it <i>mixes in</i>
+  # the functionality contained in the user-defined module
+  # specified in calls to EventMachine#connect or EventMachine#start_server.
+  # User-defined handler modules may redefine any or all of the standard
+  # methods defined here, as well as add arbitrary additional code
+  # that will also be mixed in.
+  #
+  # EventMachine manages one object inherited from EventMachine::Connection
+  # (and containing the mixed-in user code) for every network connection
+  # that is active at any given time.
+  # The event loop will automatically call methods on EventMachine::Connection
+  # objects whenever specific events occur on the corresponding connections,
+  # as described below.
+  #
+  # This class is never instantiated by user code, and does not publish an
+  # initialize method. The instance methods of EventMachine::Connection
+  # which may be called by the event loop are: post_init, receive_data,
+  # and unbind. All of the other instance methods defined here are called
+  # only by user code.
+  #
+  class Connection
+    attr_accessor :signature # :nodoc:
+
+    # Override .new so subclasses don't have to call super and can ignore
+    # connection-specific arguments
+    #
+    def self.new(sig, *args) #:nodoc:
+      allocate.instance_eval do
+        # Store signature
+        @signature = sig
+        associate_callback_target sig
+
+        # Call a superclass's #initialize if it has one
+        initialize(*args)
+
+        # post initialize callback
+        post_init
+
+        self
+      end
+    end
+
+    # Stubbed initialize so legacy superclasses can safely call super
+    #
+    def initialize(*args) #:nodoc:
+    end
+
+    # def associate_callback_target(sig) #:nodoc:
+    #   # no-op for the time being, to match similar no-op in rubymain.cpp
+    # end
+
+    # EventMachine::Connection#post_init is called by the event loop
+    # immediately after the network connection has been established,
+    # and before resumption of the network loop.
+    # This method is generally not called by user code, but is called automatically
+    # by the event loop. The base-class implementation is a no-op.
+    # This is a very good place to initialize instance variables that will
+    # be used throughout the lifetime of the network connection.
+    #
+    def post_init
+    end
+
+    # EventMachine::Connection#receive_data is called by the event loop
+    # whenever data has been received by the network connection.
+    # It is never called by user code.
+    # receive_data is called with a single parameter, a String containing
+    # the network protocol data, which may of course be binary. You will
+    # generally redefine this method to perform your own processing of the incoming data.
+    #
+    # Here's a key point which is essential to understanding the event-driven
+    # programming model: <i>EventMachine knows absolutely nothing about the protocol
+    # which your code implements.</i> You must not make any assumptions about
+    # the size of the incoming data packets, or about their alignment on any
+    # particular intra-message or PDU boundaries (such as line breaks).
+    # receive_data can and will send you arbitrary chunks of data, with the
+    # only guarantee being that the data is presented to your code in the order
+    # it was collected from the network. Don't even assume that the chunks of
+    # data will correspond to network packets, as EventMachine can and will coalesce
+    # several incoming packets into one, to improve performance. The implication for your
+    # code is that you generally will need to implement some kind of a state machine
+    # in your redefined implementation of receive_data. For a better understanding
+    # of this, read through the examples of specific protocol handlers in EventMachine::Protocols
+    #
+    # The base-class implementation of receive_data (which will be invoked if
+    # you don't redefine it) simply prints the size of each incoming data packet
+    # to stdout.
+    #
+    def receive_data data
+      puts "............>>>#{data.length}"
+    end
+
+    # #ssl_handshake_completed is called by EventMachine when the SSL/TLS handshake has
+    # been completed, as a result of calling #start_tls to initiate SSL/TLS on the connection.
+    #
+    # This callback exists because #post_init and #connection_completed are <b>not</b> reliable
+    # for indicating when an SSL/TLS connection is ready to have it's certificate queried for.
+    #
+    # See #get_peer_cert for application and example.
+    def ssl_handshake_completed
+    end
+
+    # #ssl_verify_peer is called by EventMachine when :verify_peer => true has been passed to #start_tls.
+    # It will be called with each certificate in the certificate chain provided by the remote peer.
+    # The cert will be passed as a String in PEM format, the same as in #get_peer_cert. It is up to user defined
+    # code to perform a check on the certificates. The return value from this callback is used to accept or deny the peer.
+    # A return value that is not nil or false triggers acceptance. If the peer is not accepted, the connection
+    # will be subsequently closed. See 'tests/test_ssl_verify.rb' for a simple example.
+    def ssl_verify_peer(cert)
+    end
+
+    # EventMachine::Connection#unbind is called by the framework whenever a connection
+    # (either a server or client connection) is closed. The close can occur because
+    # your code intentionally closes it (see close_connection and close_connection_after_writing),
+    # because the remote peer closed the connection, or because of a network error.
+    # You may not assume that the network connection is still open and able to send or
+    # receive data when the callback to unbind is made. This is intended only to give
+    # you a chance to clean up associations your code may have made to the connection
+    # object while it was open.
+    #
+    def unbind
+    end
+
+    # EventMachine::Connection#proxy_target_unbound is called by the reactor after attempting
+    # to relay incoming data to a descriptor (set as a proxy target descriptor with
+    # EventMachine::enable_proxy) that has already been closed.
+    def proxy_target_unbound
+    end
+
+    # EventMachine::Connection#proxy_incoming_to is called only by user code. It sets up
+    # a low-level proxy relay for all data inbound for this connection, to the connection given
+    # as the argument. This is essentially just a helper method for enable_proxy.
+    # See EventMachine::enable_proxy documentation for details.
+    def proxy_incoming_to(conn,bufsize=0)
+      EventMachine::enable_proxy(self, conn, bufsize)
+    end
+
+    # Helper method for EventMachine::disable_proxy(self)
+    def stop_proxying
+      EventMachine::disable_proxy(self)
+    end
+
+    # EventMachine::Connection#close_connection is called only by user code, and never
+    # by the event loop. You may call this method against a connection object in any
+    # callback handler, whether or not the callback was made against the connection
+    # you want to close. close_connection <i>schedules</i> the connection to be closed
+    # at the next available opportunity within the event loop. You may not assume that
+    # the connection is closed when close_connection returns. In particular, the framework
+    # will callback the unbind method for the particular connection at a point shortly
+    # after you call close_connection. You may assume that the unbind callback will
+    # take place sometime after your call to close_connection completes. In other words,
+    # the unbind callback will not re-enter your code "inside" of your call to close_connection.
+    # However, it's not guaranteed that a future version of EventMachine will not change
+    # this behavior.
+    #
+    # close_connection will <i>silently discard</i> any outbound data which you have
+    # sent to the connection using EventMachine::Connection#send_data but which has not
+    # yet been sent across the network. If you want to avoid this behavior, use
+    # EventMachine::Connection#close_connection_after_writing.
+    #
+    def close_connection after_writing = false
+      EventMachine::close_connection @signature, after_writing
+    end
+
+    # EventMachine::Connection#detach will remove the given connection from the event loop.
+    # The connection's socket remains open and its file descriptor number is returned
+    def detach
+      EventMachine::detach_fd @signature
+    end
+
+    def get_sock_opt level, option
+      EventMachine::get_sock_opt @signature, level, option
+    end
+
+    # EventMachine::Connection#close_connection_after_writing is a variant of close_connection.
+    # All of the descriptive comments given for close_connection also apply to
+    # close_connection_after_writing, <i>with one exception:</i> If the connection has
+    # outbound data sent using send_dat but which has not yet been sent across the network,
+    # close_connection_after_writing will schedule the connection to be closed <i>after</i>
+    # all of the outbound data has been safely written to the remote peer.
+    #
+    # Depending on the amount of outgoing data and the speed of the network,
+    # considerable time may elapse between your call to close_connection_after_writing
+    # and the actual closing of the socket (at which time the unbind callback will be called
+    # by the event loop). During this time, you <i>may not</i> call send_data to transmit
+    # additional data (that is, the connection is closed for further writes). In very
+    # rare cases, you may experience a receive_data callback after your call to close_connection_after_writing,
+    # depending on whether incoming data was in the process of being received on the connection
+    # at the moment when you called close_connection_after_writing. Your protocol handler must
+    # be prepared to properly deal with such data (probably by ignoring it).
+    #
+    def close_connection_after_writing
+      close_connection true
+    end
+
+    # EventMachine::Connection#send_data is only called by user code, never by
+    # the event loop. You call this method to send data to the remote end of the
+    # network connection. send_data is called with a single String argument, which
+    # may of course contain binary data. You can call send_data any number of times.
+    # send_data is an instance method of an object derived from EventMachine::Connection
+    # and containing your mixed-in handler code), so if you call it without qualification
+    # within a callback function, the data will be sent to the same network connection
+    # that generated the callback. Calling self.send_data is exactly equivalent.
+    #
+    # You can also call send_data to write to a connection <i>other than the one
+    # whose callback you are calling send_data from.</i> This is done by recording
+    # the value of the connection in any callback function (the value self), in any
+    # variable visible to other callback invocations on the same or different
+    # connection objects. (Need an example to make that clear.)
+    #
+    def send_data data
+      data = data.to_s
+      size = data.bytesize if data.respond_to?(:bytesize)
+      size ||= data.size
+      EventMachine::send_data @signature, data, size
+    end
+
+    # Returns true if the connection is in an error state, false otherwise.
+    # In general, you can detect the occurrence of communication errors or unexpected
+    # disconnection by the remote peer by handing the #unbind method. In some cases, however,
+    # it's useful to check the status of the connection using #error? before attempting to send data.
+    # This function is synchronous: it will return immediately without blocking.
+    #
+    #
+    def error?
+      EventMachine::report_connection_error_status(@signature) != 0
+    end
+
+    # #connection_completed is called by the event loop when a remote TCP connection
+    # attempt completes successfully. You can expect to get this notification after calls
+    # to EventMachine#connect. Remember that EventMachine makes remote connections
+    # asynchronously, just as with any other kind of network event. #connection_completed
+    # is intended primarily to assist with network diagnostics. For normal protocol
+    # handling, use #post_init to perform initial work on a new connection (such as
+    # send an initial set of data).
+    # #post_init will always be called. #connection_completed will only be called in case
+    # of a successful completion. A connection-attempt which fails will receive a call
+    # to #unbind after the failure.
+    def connection_completed
+    end
+
+    # Call #start_tls at any point to initiate TLS encryption on connected streams.
+    # The method is smart enough to know whether it should perform a server-side
+    # or a client-side handshake. An appropriate place to call #start_tls is in
+    # your redefined #post_init method, or in the #connection_completed handler for
+    # an outbound connection.
+    #
+    # #start_tls takes an optional parameter hash that allows you to specify certificate
+    # and other options to be used with this Connection object. Here are the currently-supported
+    # options:
+    #
+    # * :cert_chain_file :
+    # takes a String, which is interpreted as the name of a readable file in the
+    # local filesystem. The file is expected to contain a chain of X509 certificates in
+    # PEM format, with the most-resolved certificate at the top of the file, successive
+    # intermediate certs in the middle, and the root (or CA) cert at the bottom.
+    #
+    # * :private_key_file :
+    # takes a String, which is interpreted as the name of a readable file in the
+    # local filesystem. The file must contain a private key in PEM format.
+    #
+    # * :verify_peer :
+    # takes either true or false. Default is false. This indicates whether a server should request a
+    # certificate from a peer, to be verified by user code. If true, the #ssl_verify_peer callback
+    # on the Connection object is called with each certificate in the certificate chain provided by
+    # the peer. See documentation on #ssl_verify_peer for how to use this.
+    #
+    # === Usage example:
+    #
+    #  require 'rubygems'
+    #  require 'eventmachine'
+    #
+    #  module Handler
+    #    def post_init
+    #      start_tls(:private_key_file => '/tmp/server.key', :cert_chain_file => '/tmp/server.crt', :verify_peer => false)
+    #    end
+    #  end
+    #
+    #  EM.run {
+    #    EM.start_server("127.0.0.1", 9999, Handler)
+    #  }
+    #
+    #--
+    # TODO: support passing an encryption parameter, which can be string or Proc, to get a passphrase
+    # for encrypted private keys.
+    # TODO: support passing key material via raw strings or Procs that return strings instead of
+    # just filenames.
+    # What will get nasty is whether we have to define a location for storing this stuff as files.
+    # In general, the OpenSSL interfaces for dealing with certs and keys in files are much better
+    # behaved than the ones for raw chunks of memory.
+    #
+    def start_tls args={}
+      priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer)
+
+      [priv_key, cert_chain].each do |file|
+        next if file.nil? or file.empty?
+        raise FileNotFoundException,
+          "Could not find #{file} for start_tls" unless File.exists? file
+      end
+
+      EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer)
+      EventMachine::start_tls @signature
+    end
+
+    # If SSL/TLS is active on the connection, #get_peer_cert returns the remote X509 certificate
+    # as a String, in the popular PEM format. This can then be used for arbitrary validation
+    # of a peer's certificate in your code.
+    #
+    # This should be called in/after the #ssl_handshake_completed callback, which indicates
+    # that SSL/TLS is active. Using this callback is important, because the certificate may not
+    # be available until the time it is executed. Using #post_init or #connection_completed is
+    # not adequate, because the SSL handshake may still be taking place.
+    #
+    # #get_peer_cert will return <b>nil</b> if:
+    #
+    # * EventMachine is not built with OpenSSL support
+    # * SSL/TLS is not active on the connection
+    # * SSL/TLS handshake is not yet complete
+    # * Remote peer for any other reason has not presented a certificate
+    #
+    # === Example:
+    #
+    #  module Handler
+    #
+    #   def post_init
+    #     puts "Starting TLS"
+    #     start_tls
+    #   end
+    #
+    #   def ssl_handshake_completed
+    #     puts get_peer_cert
+    #     close_connection
+    #   end
+    #
+    #   def unbind
+    #     EventMachine::stop_event_loop
+    #   end
+    #
+    #  end
+    #
+    #  EM.run {
+    #   EventMachine::connect "mail.google.com", 443, Handler
+    #  }
+    #
+    # Output:
+    #  -----BEGIN CERTIFICATE-----
+    #  MIIDIjCCAougAwIBAgIQbldpChBPqv+BdPg4iwgN8TANBgkqhkiG9w0BAQUFADBM
+    #  MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
+    #  THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wODA1MDIxNjMyNTRaFw0w
+    #  OTA1MDIxNjMyNTRaMGkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
+    #  MRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRgw
+    #  FgYDVQQDEw9tYWlsLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+    #  AoGBALlkxdh2QXegdElukCSOV2+8PKiONIS+8Tu9K7MQsYpqtLNC860zwOPQ2NLI
+    #  3Zp4jwuXVTrtzGuiqf5Jioh35Ig3CqDXtLyZoypjZUQcq4mlLzHlhIQ4EhSjDmA7
+    #  Ffw9y3ckSOQgdBQWNLbquHh9AbEUjmhkrYxIqKXeCnRKhv6nAgMBAAGjgecwgeQw
+    #  KAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwNgYDVR0f
+    #  BC8wLTAroCmgJ4YlaHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVNHQ0NBLmNy
+    #  bDByBggrBgEFBQcBAQRmMGQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0
+    #  ZS5jb20wPgYIKwYBBQUHMAKGMmh0dHA6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0
+    #  b3J5L1RoYXd0ZV9TR0NfQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEF
+    #  BQADgYEAsRwpLg1dgCR1gYDK185MFGukXMeQFUvhGqF8eT/CjpdvezyKVuz84gSu
+    #  6ccMXgcPQZGQN/F4Xug+Q01eccJjRSVfdvR5qwpqCj+6BFl5oiKDBsveSkrmL5dz
+    #  s2bn7TdTSYKcLeBkjXxDLHGBqLJ6TNCJ3c4/cbbG5JhGvoema94=
+    #  -----END CERTIFICATE-----
+    #
+    # You can do whatever you want with the certificate String, such as load it
+    # as a certificate object using the OpenSSL library, and check it's fields.
+    def get_peer_cert
+      EventMachine::get_peer_cert @signature
+    end
+
+
+    # send_datagram is for sending UDP messages.
+    # This method may be called from any Connection object that refers
+    # to an open datagram socket (see EventMachine#open_datagram_socket).
+    # The method sends a UDP (datagram) packet containing the data you specify,
+    # to a remote peer specified by the IP address and port that you give
+    # as parameters to the method.
+    # Observe that you may send a zero-length packet (empty string).
+    # However, you may not send an arbitrarily-large data packet because
+    # your operating system will enforce a platform-specific limit on
+    # the size of the outbound packet. (Your kernel
+    # will respond in a platform-specific way if you send an overlarge
+    # packet: some will send a truncated packet, some will complain, and
+    # some will silently drop your request).
+    # On LANs, it's usually OK to send datagrams up to about 4000 bytes in length,
+    # but to be really safe, send messages smaller than the Ethernet-packet
+    # size (typically about 1400 bytes). Some very restrictive WANs
+    # will either drop or truncate packets larger than about 500 bytes.
+    #--
+    # Added the Integer wrapper around the port parameter per suggestion by
+    # Matthieu Riou, after he passed a String and spent hours tearing his hair out.
+    #
+    def send_datagram data, recipient_address, recipient_port
+      data = data.to_s
+      EventMachine::send_datagram @signature, data, data.length, recipient_address, Integer(recipient_port)
+    end
+
+
+    # #get_peername is used with stream-connections to obtain the identity
+    # of the remotely-connected peer. If a peername is available, this method
+    # returns a sockaddr structure. The method returns nil if no peername is available.
+    # You can use Socket.unpack_sockaddr_in and its variants to obtain the
+    # values contained in the peername structure returned from #get_peername.
+    #
+    #  require 'socket'
+    #  module Handler
+    #    def receive_data data
+    #      port, ip = Socket.unpack_sockaddr_in(get_peername)
+    #      puts "got #{data.inspect} from #{ip}:#{port}"
+    #    end
+    #  end
+    def get_peername
+      EventMachine::get_peername @signature
+    end
+
+    # #get_sockname is used with stream-connections to obtain the identity
+    # of the local side of the connection. If a local name is available, this method
+    # returns a sockaddr structure. The method returns nil if no local name is available.
+    # You can use Socket#unpack_sockaddr_in and its variants to obtain the
+    # values contained in the local-name structure returned from #get_sockname.
+    def get_sockname
+      EventMachine::get_sockname @signature
+    end
+
+    # Returns the PID (kernel process identifier) of a subprocess
+    # associated with this Connection object. For use with EventMachine#popen
+    # and similar methods. Returns nil when there is no meaningful subprocess.
+    #--
+    #
+    def get_pid
+      EventMachine::get_subprocess_pid @signature
+    end
+
+    # Returns a subprocess exit status. Only useful for #popen. Call it in your
+    # #unbind handler.
+    #
+    def get_status
+      EventMachine::get_subprocess_status @signature
+    end
+
+    # comm_inactivity_timeout returns the current value (float in seconds) of the inactivity-timeout
+    # property of network-connection and datagram-socket objects. A nonzero value
+    # indicates that the connection or socket will automatically be closed if no read or write
+    # activity takes place for at least that number of seconds.
+    # A zero value (the default) specifies that no automatic timeout will take place.
+    def comm_inactivity_timeout
+      EventMachine::get_comm_inactivity_timeout @signature
+    end
+
+    # Alias for #set_comm_inactivity_timeout.
+    def comm_inactivity_timeout= value
+      self.set_comm_inactivity_timeout value
+    end
+
+    # comm_inactivity_timeout= allows you to set the inactivity-timeout property for
+    # a network connection or datagram socket. Specify a non-negative float value in seconds.
+    # If the value is greater than zero, the connection or socket will automatically be closed
+    # if no read or write activity takes place for at least that number of seconds.
+    # Specify a value of zero to indicate that no automatic timeout should take place.
+    # Zero is the default value.
+    def set_comm_inactivity_timeout value
+      EventMachine::set_comm_inactivity_timeout @signature, value.to_f
+    end
+
+    # pending_connect_timeout is the duration after which a TCP connection in the connecting 
+    # state will fail. It is important to distinguish this value from comm_inactivity_timeout,
+    # which looks at how long since data was passed on an already established connection.
+    # The value is a float in seconds.
+    def pending_connect_timeout
+      EventMachine::get_pending_connect_timeout @signature
+    end
+
+    # Alias for #set_pending_connect_timeout.
+    def pending_connect_timeout= value
+      self.set_pending_connect_timeout value
+    end
+
+    # set_pending_connect_timeout sets the duration after which a TCP connection in a
+    # connecting state will fail. Takes a float in seconds.
+    def set_pending_connect_timeout value
+      EventMachine::set_pending_connect_timeout @signature, value.to_f
+    end
+
+    # Reconnect to a given host/port with the current EventMachine::Connection instance
+    def reconnect server, port
+      EventMachine::reconnect server, port, self
+    end
+
+
+    # Like EventMachine::Connection#send_data, this sends data to the remote end of
+    # the network connection.  EventMachine::Connection@send_file_data takes a
+    # filename as an argument, though, and sends the contents of the file, in one
+    # chunk. Contributed by Kirk Haines.
+    #
+    def send_file_data filename
+      EventMachine::send_file_data @signature, filename
+    end
+
+    # Open a file on the filesystem and send it to the remote peer. This returns an
+    # object of type EventMachine::Deferrable. The object's callbacks will be executed
+    # on the reactor main thread when the file has been completely scheduled for
+    # transmission to the remote peer. Its errbacks will be called in case of an error
+    # (such as file-not-found). #stream_file_data employs various strategems to achieve
+    # the fastest possible performance, balanced against minimum consumption of memory.
+    #
+    # You can control the behavior of #stream_file_data with the optional arguments parameter.
+    # Currently-supported arguments are:
+    # :http_chunks, a boolean flag which defaults false. If true, this flag streams the
+    # file data in a format compatible with the HTTP chunked-transfer encoding.
+    #
+    # Warning: this feature has an implicit dependency on an outboard extension,
+    # evma_fastfilereader. You must install this extension in order to use #stream_file_data
+    # with files larger than a certain size (currently 8192 bytes).
+    #
+    def stream_file_data filename, args={}
+      EventMachine::FileStreamer.new( self, filename, args )
+    end
+
+    # Enable notify_readable callbacks on this connection. Only possible if the connection was created
+    # using EM.attach and had notify_readable/notify_writable defined on the handler.
+    def notify_readable= mode
+      EventMachine::set_notify_readable @signature, mode
+    end
+
+    # Returns true if the connection is being watched for readability.
+    def notify_readable?
+      EventMachine::is_notify_readable @signature
+    end
+
+    # Enable notify_writable callbacks on this connection. Only possible if the connection was created
+    # using EM.attach and had notify_readable/notify_writable defined on the handler.
+    def notify_writable= mode
+      EventMachine::set_notify_writable @signature, mode
+    end
+
+    # Returns true if the connection is being watched for writability.
+    def notify_writable?
+      EventMachine::is_notify_writable @signature
+    end
+
+    # Pause a connection so that #send_data and #receive_data events are not fired until #resume is called.
+    def pause
+      EventMachine::pause_connection @signature
+    end
+
+    # Resume a connection's #send_data and #receive_data events.
+    def resume
+      EventMachine::resume_connection @signature
+    end
+
+    # True if the connect was paused using #pause.
+    def paused?
+      EventMachine::connection_paused? @signature
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/deferrable.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/deferrable.rb
new file mode 100644 (file)
index 0000000..1cb7018
--- /dev/null
@@ -0,0 +1,192 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 16 Jul 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+module EventMachine
+  module Deferrable
+
+    # Specify a block to be executed if and when the Deferrable object receives
+    # a status of :succeeded. See #set_deferred_status for more information.
+    #
+    # Calling this method on a Deferrable object whose status is not yet known
+    # will cause the callback block to be stored on an internal list.
+    # If you call this method on a Deferrable whose status is :succeeded, the
+    # block will be executed immediately, receiving the parameters given to the
+    # prior #set_deferred_status call.
+    #
+    #--
+    # If there is no status, add a callback to an internal list.
+    # If status is succeeded, execute the callback immediately.
+    # If status is failed, do nothing.
+    #
+    def callback &block
+      return unless block
+      @deferred_status ||= :unknown
+      if @deferred_status == :succeeded
+        block.call(*@deferred_args)
+      elsif @deferred_status != :failed
+        @callbacks ||= []
+        @callbacks.unshift block # << block
+      end
+    end
+
+    # Specify a block to be executed if and when the Deferrable object receives
+    # a status of :failed. See #set_deferred_status for more information.
+    #--
+    # If there is no status, add an errback to an internal list.
+    # If status is failed, execute the errback immediately.
+    # If status is succeeded, do nothing.
+    #
+    def errback &block
+      return unless block
+      @deferred_status ||= :unknown
+      if @deferred_status == :failed
+        block.call(*@deferred_args)
+      elsif @deferred_status != :succeeded
+        @errbacks ||= []
+        @errbacks.unshift block # << block
+      end
+    end
+
+    # Sets the "disposition" (status) of the Deferrable object. See also the large set of
+    # sugarings for this method.
+    # Note that if you call this method without arguments,
+    # no arguments will be passed to the callback/errback.
+    # If the user has coded these with arguments, then the
+    # user code will throw an argument exception.
+    # Implementors of deferrable classes <b>must</b>
+    # document the arguments they will supply to user callbacks.
+    #
+    # OBSERVE SOMETHING VERY SPECIAL here: you may call this method even
+    # on the INSIDE of a callback. This is very useful when a previously-registered
+    # callback wants to change the parameters that will be passed to subsequently-registered
+    # ones.
+    #
+    # You may give either :succeeded or :failed as the status argument.
+    #
+    # If you pass :succeeded, then all of the blocks passed to the object using the #callback
+    # method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
+    # passed to the object using #errback will be discarded.
+    #
+    # If you pass :failed, then all of the blocks passed to the object using the #errback
+    # method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
+    # passed to the object using # callback will be discarded.
+    #
+    # If you pass any arguments to #set_deferred_status in addition to the status argument,
+    # they will be passed as arguments to any callbacks or errbacks that are executed.
+    # It's your responsibility to ensure that the argument lists specified in your callbacks and
+    # errbacks match the arguments given in calls to #set_deferred_status, otherwise Ruby will raise
+    # an ArgumentError.
+    #
+    #--
+    # We're shifting callbacks off and discarding them as we execute them.
+    # This is valid because by definition callbacks are executed no more than
+    # once. It also has the magic effect of permitting recursive calls, which
+    # means that a callback can call #set_deferred_status and change the parameters
+    # that will be sent to subsequent callbacks down the chain.
+    #
+    # Changed @callbacks and @errbacks from push/shift to unshift/pop, per suggestion
+    # by Kirk Haines, to work around the memory leak bug that still exists in many Ruby
+    # versions.
+    #
+    # Changed 15Sep07: after processing callbacks or errbacks, CLEAR the other set of
+    # handlers. This gets us a little closer to the behavior of Twisted's "deferred,"
+    # which only allows status to be set once. Prior to making this change, it was possible
+    # to "succeed" a Deferrable (triggering its callbacks), and then immediately "fail" it,
+    # triggering its errbacks! That is clearly undesirable, but it's just as undesirable
+    # to raise an exception is status is set more than once on a Deferrable. The latter
+    # behavior would invalidate the idiom of resetting arguments by setting status from
+    # within a callback or errback, but more seriously it would cause spurious errors
+    # if a Deferrable was timed out and then an attempt was made to succeed it. See the
+    # comments under the new method #timeout.
+    #
+    def set_deferred_status status, *args
+      cancel_timeout
+      @errbacks ||= nil
+      @callbacks ||= nil
+      @deferred_status = status
+      @deferred_args = args
+      case @deferred_status
+      when :succeeded
+        if @callbacks
+          while cb = @callbacks.pop
+            cb.call(*@deferred_args)
+          end
+        end
+        @errbacks.clear if @errbacks
+      when :failed
+        if @errbacks
+          while eb = @errbacks.pop
+            eb.call(*@deferred_args)
+          end
+        end
+        @callbacks.clear if @callbacks
+      end
+    end
+
+
+    # Setting a timeout on a Deferrable causes it to go into the failed state after
+    # the Timeout expires (passing no arguments to the object's errbacks).
+    # Setting the status at any time prior to a call to the expiration of the timeout
+    # will cause the timer to be cancelled.
+    def timeout seconds
+      cancel_timeout
+      me = self
+      @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail}
+    end
+
+    # Cancels an outstanding timeout if any. Undoes the action of #timeout.
+    #
+    def cancel_timeout
+      @deferred_timeout ||= nil
+      if @deferred_timeout
+        @deferred_timeout.cancel
+        @deferred_timeout = nil
+      end
+    end
+
+
+    # Sugar for set_deferred_status(:succeeded, ...)
+    #
+    def succeed *args
+      set_deferred_status :succeeded, *args
+    end
+    alias set_deferred_success succeed
+
+    # Sugar for set_deferred_status(:failed, ...)
+    #
+    def fail *args
+      set_deferred_status :failed, *args
+    end
+    alias set_deferred_failure fail
+  end
+
+
+  # DefaultDeferrable is an otherwise empty class that includes Deferrable.
+  # This is very useful when you just need to return a Deferrable object
+  # as a way of communicating deferred status to some other part of a program.
+  class DefaultDeferrable
+    include Deferrable
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/file_watch.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/file_watch.rb
new file mode 100644 (file)
index 0000000..eb3b84e
--- /dev/null
@@ -0,0 +1,54 @@
+module EventMachine\r
+\r
+  # This is subclassed from EventMachine::Connection for use with the file monitoring API. Read the\r
+  # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_file.\r
+  class FileWatch < Connection\r
+    # :stopdoc:\r
+    Cmodified = 'modified'.freeze\r
+    Cdeleted = 'deleted'.freeze\r
+    Cmoved = 'moved'.freeze\r
+    # :startdoc:\r
+\r
+    def receive_data(data) #:nodoc:\r
+      case data\r
+      when Cmodified\r
+        file_modified\r
+      when Cdeleted\r
+        file_deleted\r
+      when Cmoved\r
+        file_moved\r
+      end\r
+    end\r
+\r
+    # Returns the path that EventMachine::watch_file was originally called with. The current implementation\r
+    # does not pick up on the new filename after a rename occurs.\r
+    def path\r
+      @path\r
+    end\r
+\r
+    # Should be redefined with the user's custom callback that will be fired when the file is modified.\r
+    def file_modified\r
+    end\r
+\r
+    # Should be redefined with the user's custom callback that will be fired when the file is deleted.\r
+    # When the file is deleted, stop_watching will be called after this to make sure everything is\r
+    # cleaned up correctly.\r
+    #\r
+    # Note that on linux (with inotify), file_deleted will not be called until all open file descriptors to\r
+    # the file have been closed.\r
+    def file_deleted\r
+    end\r
+\r
+    # Should be redefined with the user's custom callback that will be fired when the file is moved or renamed.\r
+    def file_moved\r
+    end\r
+\r
+    # Discontinue monitoring of the file.\r
+    # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing unbind.\r
+    # This will be called automatically when a file is deleted. User code may call it as well.\r
+    def stop_watching\r
+      EventMachine::unwatch_filename(@signature)\r
+    end\r
+  end\r
+\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/future.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/future.rb
new file mode 100644 (file)
index 0000000..8a6a894
--- /dev/null
@@ -0,0 +1,61 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 Jul 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+\r
+#--\r
+# This defines EventMachine::Deferrable#future, which requires\r
+# that the rest of EventMachine::Deferrable has already been seen.\r
+# (It's in deferrable.rb.)\r
+\r
+module EventMachine\r
+    module Deferrable\r
+\r
+      # A future is a sugaring of a typical deferrable usage.\r
+      #--\r
+      # Evaluate arg (which may be an expression or a block).\r
+      # What's the class of arg?\r
+      # If arg is an ordinary expression, then return it.\r
+      # If arg is deferrable (responds to :set_deferred_status),\r
+      # then look at the arguments. If either callback or errback\r
+      # are defined, then use them. If neither are defined, then\r
+      # use the supplied block (if any) as the callback.\r
+      # Then return arg.\r
+      def self.future arg, cb=nil, eb=nil, &blk\r
+        arg = arg.call if arg.respond_to?(:call)\r
+\r
+        if arg.respond_to?(:set_deferred_status)\r
+          if cb || eb\r
+            arg.callback(&cb) if cb\r
+            arg.errback(&eb) if eb\r
+          else\r
+            arg.callback(&blk) if blk\r
+          end\r
+        end\r
+\r
+        arg\r
+      end\r
+\r
+    end\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/messages.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/messages.rb
new file mode 100644 (file)
index 0000000..fe41b5a
--- /dev/null
@@ -0,0 +1,66 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 Jul 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+\r
+=begin\r
+\r
+Message Routing in EventMachine.\r
+\r
+The goal here is to enable "routing points," objects that can send and receive\r
+"messages," which are delimited streams of bytes. The boundaries of a message\r
+are preserved as it passes through the reactor system.\r
+\r
+There will be several module methods defined in EventMachine to create route-point\r
+objects (which will probably have a base class of EventMachine::MessageRouter\r
+until someone suggests a better name).\r
+\r
+As with I/O objects, routing objects will receive events by having the router\r
+core call methods on them. And of course user code can and will define handlers\r
+to deal with events of interest.\r
+\r
+The message router base class only really needs a receive_message method. There will\r
+be an EM module-method to send messages, in addition to the module methods to create\r
+the various kinds of message receivers.\r
+\r
+The simplest kind of message receiver object can receive messages by being named \r
+explicitly in a parameter to EM#send_message. More sophisticated receivers can define\r
+pub-sub selectors and message-queue names. And they can also define channels for\r
+route-points in other processes or even on other machines.\r
+\r
+A message is NOT a marshallable entity. Rather, it's a chunk of flat content more like\r
+an Erlang message. Initially, all content submitted for transmission as a message will\r
+have the to_s method called on it. Eventually, we'll be able to transmit certain structured\r
+data types (XML and YAML documents, Structs within limits) and have them reconstructed\r
+on the other end.\r
+\r
+A fundamental goal of the message-routing capability is to interoperate seamlessly with\r
+external systems, including non-Ruby systems like ActiveMQ. We will define various protocol\r
+handlers for things like Stomp and possibly AMQP, but these will be wrapped up and hidden\r
+from the users of the basic routing capability.\r
+\r
+As with Erlang, a critical goal is for programs that are built to use message-passing to work\r
+WITHOUT CHANGE when the code is re-based on a multi-process system.\r
+\r
+=end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/process_watch.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/process_watch.rb
new file mode 100644 (file)
index 0000000..d8b1062
--- /dev/null
@@ -0,0 +1,44 @@
+module EventMachine\r
+\r
+  # This is subclassed from EventMachine::Connection for use with the process monitoring API. Read the\r
+  # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_process.\r
+  class ProcessWatch < Connection\r
+    # :stopdoc:\r
+    Cfork = 'fork'.freeze\r
+    Cexit = 'exit'.freeze\r
+    # :startdoc:\r
+\r
+    def receive_data(data) # :nodoc:\r
+      case data\r
+      when Cfork\r
+        process_forked\r
+      when Cexit\r
+        process_exited\r
+      end\r
+    end\r
+\r
+    # Returns the pid that EventMachine::watch_process was originally called with.\r
+    def pid\r
+      @pid\r
+    end\r
+\r
+    # Should be redefined with the user's custom callback that will be fired when the prcess is forked.\r
+    #\r
+    # There is currently not an easy way to get the pid of the forked child.\r
+    def process_forked\r
+    end\r
+\r
+    # Should be redefined with the user's custom callback that will be fired when the process exits.\r
+    #\r
+    # stop_watching is called automatically after this callback\r
+    def process_exited\r
+    end\r
+\r
+    # Discontinue monitoring of the process.\r
+    # This will be called automatically when a process dies. User code may call it as well.\r
+    def stop_watching\r
+      EventMachine::unwatch_pid(@signature)\r
+    end\r
+  end\r
+\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/processes.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/processes.rb
new file mode 100644 (file)
index 0000000..ef4dbbf
--- /dev/null
@@ -0,0 +1,119 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 13 Dec 07
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+
+module EventMachine
+
+  # EM::DeferrableChildProcess is a sugaring of a common use-case
+  # involving EM::popen.
+  # Call the #open method on EM::DeferrableChildProcess, passing
+  # a command-string. #open immediately returns an EM::Deferrable
+  # object. It also schedules the forking of a child process, which
+  # will execute the command passed to #open.
+  # When the forked child terminates, the Deferrable will be signalled
+  # and execute its callbacks, passing the data that the child process
+  # wrote to stdout.
+  #
+  class DeferrableChildProcess < EventMachine::Connection
+    include EventMachine::Deferrable
+
+    def initialize # :nodoc:
+      super
+      @data = []
+    end
+
+    # Sugars a common use-case involving forked child processes.
+    # #open takes a String argument containing an shell command
+    # string (including arguments if desired). #open immediately
+    # returns an EventMachine::Deferrable object, without blocking.
+    #
+    # It also invokes EventMachine#popen to run the passed-in
+    # command in a forked child process.
+    #
+    # When the forked child terminates, the Deferrable that
+    # #open calls its callbacks, passing the data returned
+    # from the child process.
+    #
+    def self.open cmd
+      EventMachine.popen( cmd, DeferrableChildProcess )
+    end
+
+    def receive_data data # :nodoc:
+      @data << data
+    end
+
+    def unbind # :nodoc:
+      succeed( @data.join )
+    end
+  end
+
+  class SystemCmd < EventMachine::Connection # :nodoc:
+    def initialize cb
+      @cb = cb
+      @output = []
+    end
+    def receive_data data
+      @output << data
+    end
+    def unbind
+      @cb.call @output.join(''), get_status if @cb
+    end
+  end
+
+  # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a
+  # single string argument for the command and performs no shell expansion.
+  #
+  # The block or proc passed to EM::system is called with two arguments: the output generated by the command,
+  # and a Process::Status that contains information about the command's execution.
+  #
+  #  EM.run{
+  #    EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 }
+  #  }
+  #
+  # You can also supply an additional proc to send some data to the process:
+  #
+  #  EM.run{
+  #    EM.system('sh', proc{ |process|
+  #      process.send_data("echo hello\n")
+  #      process.send_data("exit\n")
+  #    }, proc{ |out,status|
+  #      puts(out)
+  #    })
+  #  }
+  #
+  # Like EventMachine.popen, EventMachine.system currently does not work on windows.
+  # It returns the pid of the spawned process.
+  def EventMachine::system cmd, *args, &cb
+    cb ||= args.pop if args.last.is_a? Proc
+    init = args.pop if args.last.is_a? Proc
+
+    # merge remaining arguments into the command
+    cmd = ([cmd] + args.map{|a|a.to_s.dump}).join(' ')
+
+    EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c|
+      init[c] if init
+    end.signature)
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols.rb
new file mode 100644 (file)
index 0000000..d369ca6
--- /dev/null
@@ -0,0 +1,36 @@
+module EventMachine
+  # This module contains various protocol implementations, including:
+  # - HttpClient and HttpClient2
+  # - Stomp
+  # - Memcache
+  # - SmtpClient and SmtpServer
+  # - SASLauth and SASLauthclient
+  # - LineAndTextProtocol and LineText2
+  # - HeaderAndContentProtocol
+  # - Postgres3
+  # - ObjectProtocol
+  #
+  # The protocol implementations live in separate files in the protocols/ subdirectory,
+  # but are auto-loaded when they are first referenced in your application.
+  #
+  # EventMachine::Protocols is also aliased to EM::P for easier usage.
+  #
+  module Protocols
+    # TODO : various autotools are completely useless with the lack of naming
+    # convention, we need to correct that!
+    autoload :TcpConnectTester, 'em/protocols/tcptest'
+    autoload :HttpClient, 'em/protocols/httpclient'
+    autoload :HttpClient2, 'em/protocols/httpclient2'
+    autoload :LineAndTextProtocol, 'em/protocols/line_and_text'
+    autoload :HeaderAndContentProtocol, 'em/protocols/header_and_content'
+    autoload :LineText2, 'em/protocols/linetext2'
+    autoload :Stomp, 'em/protocols/stomp'
+    autoload :SmtpClient, 'em/protocols/smtpclient'
+    autoload :SmtpServer, 'em/protocols/smtpserver'
+    autoload :SASLauth, 'em/protocols/saslauth'
+    autoload :Memcache, 'em/protocols/memcache'
+    autoload :Postgres3, 'em/protocols/postgres3'
+    autoload :ObjectProtocol, 'em/protocols/object_protocol'
+    autoload :Socks4, 'em/protocols/socks4'
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/header_and_content.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/header_and_content.rb
new file mode 100644 (file)
index 0000000..575a4a6
--- /dev/null
@@ -0,0 +1,138 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 15 Nov 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    # === Usage\r
+    #\r
+    #  class RequestHandler < EM::P::HeaderAndContentProtocol\r
+    #    def receive_request headers, content\r
+    #      p [:request, headers, content]\r
+    #    end\r
+    #  end\r
+    #\r
+    #  EM.run{\r
+    #    EM.start_server 'localhost', 80, RequestHandler\r
+    #  }\r
+    #\r
+    #--\r
+    # Originally, this subclassed LineAndTextProtocol, which in\r
+    # turn relies on BufferedTokenizer, which doesn't gracefully\r
+    # handle the transitions between lines and binary text.\r
+    # Changed 13Sep08 by FCianfrocca.\r
+    class HeaderAndContentProtocol < Connection\r
+      include LineText2\r
+\r
+      ContentLengthPattern = /Content-length:\s*(\d+)/i\r
+\r
+      def initialize *args\r
+        super\r
+        init_for_request\r
+      end\r
+\r
+      def receive_line line\r
+        case @hc_mode\r
+        when :discard_blanks\r
+          unless line == ""\r
+            @hc_mode = :headers\r
+            receive_line line\r
+          end\r
+        when :headers\r
+          if line == ""\r
+            raise "unrecognized state" unless @hc_headers.length > 0\r
+            if respond_to?(:receive_headers)\r
+              receive_headers @hc_headers\r
+            end\r
+            # @hc_content_length will be nil, not 0, if there was no content-length header.\r
+            if @hc_content_length.to_i > 0\r
+              set_binary_mode @hc_content_length\r
+            else\r
+              dispatch_request\r
+            end\r
+          else\r
+            @hc_headers << line\r
+            if ContentLengthPattern =~ line\r
+              # There are some attacks that rely on sending multiple content-length\r
+              # headers. This is a crude protection, but needs to become tunable.\r
+              raise "extraneous content-length header" if @hc_content_length\r
+              @hc_content_length = $1.to_i\r
+            end\r
+            if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)\r
+              receive_first_header_line line\r
+            end\r
+          end\r
+        else\r
+          raise "internal error, unsupported mode"\r
+        end\r
+      end\r
+\r
+      def receive_binary_data text\r
+        @hc_content = text\r
+        dispatch_request\r
+      end\r
+\r
+      def dispatch_request\r
+        if respond_to?(:receive_request)\r
+          receive_request @hc_headers, @hc_content\r
+        end\r
+        init_for_request\r
+      end\r
+      private :dispatch_request\r
+\r
+      def init_for_request\r
+        @hc_mode = :discard_blanks\r
+        @hc_headers = []\r
+        # originally was @hc_headers ||= []; @hc_headers.clear to get a performance\r
+        # boost, but it's counterproductive because a subclassed handler will have to\r
+        # call dup to use the header array we pass in receive_headers.\r
+\r
+        @hc_content_length = nil\r
+        @hc_content = ""\r
+      end\r
+      private :init_for_request\r
+\r
+      # Basically a convenience method. We might create a subclass that does this\r
+      # automatically. But it's such a performance killer.\r
+      def headers_2_hash hdrs\r
+        self.class.headers_2_hash hdrs\r
+      end\r
+\r
+      class << self\r
+        def headers_2_hash hdrs\r
+          hash = {}\r
+          hdrs.each {|h|\r
+            if /\A([^\s:]+)\s*:\s*/ =~ h\r
+              tail = $'.dup\r
+              hash[ $1.downcase.gsub(/-/,"_").intern ] = tail\r
+            end\r
+          }\r
+          hash\r
+        end\r
+      end\r
+\r
+    end\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient.rb
new file mode 100644 (file)
index 0000000..7930e84
--- /dev/null
@@ -0,0 +1,263 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 July 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+\r
+\r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    # === Usage\r
+    #\r
+    #  EventMachine.run {\r
+    #    http = EventMachine::Protocols::HttpClient.request(\r
+    #      :host => server,\r
+    #      :port => 80,\r
+    #      :request => "/index.html",\r
+    #      :query_string => "parm1=value1&parm2=value2"\r
+    #    )\r
+    #    http.callback {|response|\r
+    #      puts response[:status]\r
+    #      puts response[:headers]\r
+    #      puts response[:content]\r
+    #    }\r
+    #  }\r
+    #--\r
+    # TODO:\r
+    # Add streaming so we can support enormous POSTs. Current max is 20meg.\r
+    # Timeout for connections that run too long or hang somewhere in the middle.\r
+    # Persistent connections (HTTP/1.1), may need a associated delegate object.\r
+    # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's\r
+    # DNS lookups are unbelievably slow.\r
+    # HEAD requests.\r
+    # Chunked transfer encoding.\r
+    # Convenience methods for requests. get, post, url, etc.\r
+    # SSL.\r
+    # Handle status codes like 304, 100, etc.\r
+    # Refactor this code so that protocol errors all get handled one way (an exception?),\r
+    # instead of sprinkling set_deferred_status :failed calls everywhere.\r
+    class HttpClient < Connection\r
+      include EventMachine::Deferrable\r
+\r
+      MaxPostContentLength = 20 * 1024 * 1024\r
+\r
+      # === Arg list\r
+      # :host => 'ip/dns', :port => fixnum, :verb => 'GET', :request => 'path',\r
+      # :basic_auth => {:username => '', :password => ''}, :content => 'content',\r
+      # :contenttype => 'text/plain', :query_string => '', :host_header => '',\r
+      # :cookie => ''\r
+      def self.request( args = {} )\r
+        args[:port] ||= 80\r
+        EventMachine.connect( args[:host], args[:port], self ) {|c|\r
+          # According to the docs, we will get here AFTER post_init is called.\r
+          c.instance_eval {@args = args}\r
+        }\r
+      end\r
+\r
+      def post_init\r
+        @start_time = Time.now\r
+        @data = ""\r
+        @read_state = :base\r
+      end\r
+\r
+      # We send the request when we get a connection.\r
+      # AND, we set an instance variable to indicate we passed through here.\r
+      # That allows #unbind to know whether there was a successful connection.\r
+      # NB: This naive technique won't work when we have to support multiple\r
+      # requests on a single connection.\r
+      def connection_completed\r
+        @connected = true\r
+        send_request @args\r
+      end\r
+\r
+      def send_request args\r
+        args[:verb] ||= args[:method] # Support :method as an alternative to :verb.\r
+        args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?\r
+\r
+        verb = args[:verb].to_s.upcase\r
+        unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)\r
+          set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type\r
+          return # NOTE THE EARLY RETURN, we're not sending any data.\r
+        end\r
+\r
+        request = args[:request] || "/"\r
+        unless request[0,1] == "/"\r
+          request = "/" + request\r
+        end\r
+\r
+        qs = args[:query_string] || ""\r
+        if qs.length > 0 and qs[0,1] != '?'\r
+          qs = "?" + qs\r
+        end\r
+\r
+        version = args[:version] || "1.1"\r
+\r
+        # Allow an override for the host header if it's not the connect-string.\r
+        host = args[:host_header] || args[:host] || "_"\r
+        # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.\r
+        port = args[:port]\r
+\r
+        # POST items.\r
+        postcontenttype = args[:contenttype] || "application/octet-stream"\r
+        postcontent = args[:content] || ""\r
+        raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength\r
+\r
+        # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.\r
+        # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.\r
+        req = [\r
+          "#{verb} #{request}#{qs} HTTP/#{version}",\r
+          "Host: #{host}:#{port}",\r
+          "User-agent: Ruby EventMachine",\r
+        ]\r
+\r
+          if verb == "POST" || verb == "PUT"\r
+            req << "Content-type: #{postcontenttype}"\r
+            req << "Content-length: #{postcontent.length}"\r
+          end\r
+\r
+          # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.\r
+          # Eventually we will want to deal intelligently with arrays and hashes.\r
+          if args[:cookie]\r
+            req << "Cookie: #{args[:cookie]}"\r
+          end\r
+\r
+          # Basic-auth stanza contributed by Matt Murphy.\r
+          if args[:basic_auth]\r
+            basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')\r
+            req << "Authorization: Basic #{basic_auth_string}"\r
+          end\r
+\r
+          req << ""\r
+          reqstring = req.map {|l| "#{l}\r\n"}.join\r
+          send_data reqstring\r
+\r
+          if verb == "POST" || verb == "PUT"\r
+            send_data postcontent\r
+          end\r
+      end\r
+\r
+\r
+      def receive_data data\r
+        while data and data.length > 0\r
+          case @read_state\r
+          when :base\r
+            # Perform any per-request initialization here and don't consume any data.\r
+            @data = ""\r
+            @headers = []\r
+            @content_length = nil # not zero\r
+            @content = ""\r
+            @status = nil\r
+            @read_state = :header\r
+            @connection_close = nil\r
+          when :header\r
+            ary = data.split( /\r?\n/m, 2 )\r
+            if ary.length == 2\r
+              data = ary.last\r
+              if ary.first == ""\r
+                if (@content_length and @content_length > 0) || @connection_close\r
+                  @read_state = :content\r
+                else\r
+                  dispatch_response\r
+                  @read_state = :base\r
+                end\r
+              else\r
+                @headers << ary.first\r
+                if @headers.length == 1\r
+                  parse_response_line\r
+                elsif ary.first =~ /\Acontent-length:\s*/i\r
+                  # Only take the FIRST content-length header that appears,\r
+                  # which we can distinguish because @content_length is nil.\r
+                  # TODO, it's actually a fatal error if there is more than one\r
+                  # content-length header, because the caller is presumptively\r
+                  # a bad guy. (There is an exploit that depends on multiple\r
+                  # content-length headers.)\r
+                  @content_length ||= $'.to_i\r
+                elsif ary.first =~ /\Aconnection:\s*close/i\r
+                  @connection_close = true\r
+                end\r
+              end\r
+            else\r
+              @data << data\r
+              data = ""\r
+            end\r
+          when :content\r
+            # If there was no content-length header, we have to wait until the connection\r
+            # closes. Everything we get until that point is content.\r
+            # TODO: Must impose a content-size limit, and also must implement chunking.\r
+            # Also, must support either temporary files for large content, or calling\r
+            # a content-consumer block supplied by the user.\r
+            if @content_length\r
+              bytes_needed = @content_length - @content.length\r
+              @content += data[0, bytes_needed]\r
+              data = data[bytes_needed..-1] || ""\r
+              if @content_length == @content.length\r
+                dispatch_response\r
+                @read_state = :base\r
+              end\r
+            else\r
+              @content << data\r
+              data = ""\r
+            end\r
+          end\r
+        end\r
+      end\r
+\r
+\r
+      # We get called here when we have received an HTTP response line.\r
+      # It's an opportunity to throw an exception or trigger other exceptional\r
+      # handling.\r
+      def parse_response_line\r
+        if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/\r
+          @status = $1.to_i\r
+        else\r
+          set_deferred_status :failed, {\r
+            :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.\r
+          }\r
+          close_connection\r
+        end\r
+      end\r
+      private :parse_response_line\r
+\r
+      def dispatch_response\r
+        @read_state = :base\r
+        set_deferred_status :succeeded, {\r
+          :content => @content,\r
+          :headers => @headers,\r
+          :status => @status\r
+        }\r
+        # TODO, we close the connection for now, but this is wrong for persistent clients.\r
+        close_connection\r
+      end\r
+\r
+      def unbind\r
+        if !@connected\r
+          set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.\r
+        elsif (@read_state == :content and @content_length == nil)\r
+          dispatch_response\r
+        end\r
+      end\r
+    end\r
+\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient2.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/httpclient2.rb
new file mode 100644 (file)
index 0000000..0ca875d
--- /dev/null
@@ -0,0 +1,590 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 16 July 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+module EventMachine
+  module Protocols
+
+    # === Usage
+    #
+    #  EM.run{
+    #    conn = EM::Protocols::HttpClient2.connect 'google.com', 80
+    #
+    #    req = conn.get('/')
+    #    req.callback{ |response|
+    #      p(response.status)
+    #      p(response.headers)
+    #      p(response.content)
+    #    }
+    #  }
+    class HttpClient2 < Connection
+      include LineText2
+      
+      def initialize
+        @authorization = nil
+        @closed = nil
+        @requests = nil
+      end
+
+      class Request # :nodoc:
+        include Deferrable
+
+        attr_reader :version
+        attr_reader :status
+        attr_reader :header_lines
+        attr_reader :headers
+        attr_reader :content
+        attr_reader :internal_error
+
+        def initialize conn, args
+          @conn = conn
+          @args = args
+          @header_lines = []
+          @headers = {}
+          @blanks = 0
+          @chunk_trailer = nil
+          @chunking = nil
+        end
+
+        def send_request
+          az = @args[:authorization] and az = "Authorization: #{az}\r\n"
+
+          r = [
+            "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
+            "Host: #{@args[:host_header] || "_"}\r\n",
+            az || "",
+              "\r\n"
+          ]
+          @conn.send_data r.join
+        end
+
+
+        #--
+        #
+        def receive_line ln
+          if @chunk_trailer
+            receive_chunk_trailer(ln)
+          elsif @chunking
+            receive_chunk_header(ln)
+          else
+            receive_header_line(ln)
+          end
+        end
+
+        #--
+        #
+        def receive_chunk_trailer ln
+          if ln.length == 0
+            @conn.pop_request
+            succeed(self)
+          else
+            p "Received chunk trailer line"
+          end
+        end
+
+        #--
+        # Allow up to ten blank lines before we get a real response line.
+        # Allow no more than 100 lines in the header.
+        #
+        def receive_header_line ln
+          if ln.length == 0
+            if @header_lines.length > 0
+              process_header
+            else
+              @blanks += 1
+              if @blanks > 10
+                @conn.close_connection
+              end
+            end
+          else
+            @header_lines << ln
+            if @header_lines.length > 100
+              @internal_error = :bad_header
+              @conn.close_connection
+            end
+          end
+        end
+
+        #--
+        # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
+        #
+        def receive_chunk_header ln
+          if ln.length > 0
+            chunksize = ln.to_i(16)
+            if chunksize > 0
+              @conn.set_text_mode(ln.to_i(16))
+            else
+              @content = @content ? @content.join : ''
+              @chunk_trailer = true
+            end
+          else
+            # We correctly come here after each chunk gets read.
+            # p "Got A BLANK chunk line"
+          end
+
+        end
+
+
+        #--
+        # We get a single chunk. Append it to the incoming content and switch back to line mode.
+        #
+        def receive_chunked_text text
+          # p "RECEIVED #{text.length} CHUNK"
+          (@content ||= []) << text
+        end
+
+
+        #--
+        # TODO, inefficient how we're handling this. Part of it is done so as to
+        # make sure we don't have problems in detecting chunked-encoding, content-length,
+        # etc.
+        #
+        HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+        ClenRE = /\AContent-length:\s*(\d+)/i
+        ChunkedRE = /\ATransfer-encoding:\s*chunked/i
+        ColonRE = /\:\s*/
+
+        def process_header
+          unless @header_lines.first =~ HttpResponseRE
+            @conn.close_connection
+            @internal_error = :bad_request
+          end
+          @version = $1.dup
+          @status = $2.dup.to_i
+
+          clen = nil
+          chunks = nil
+          @header_lines.each_with_index do |e,ix|
+            if ix > 0
+              hdr,val = e.split(ColonRE,2)
+              (@headers[hdr.downcase] ||= []) << val
+            end
+
+            if clen == nil and e =~ ClenRE
+              clen = $1.dup.to_i
+            end
+            if e =~ ChunkedRE
+              chunks = true
+            end
+          end
+
+          if clen
+            # If the content length is zero we should not call set_text_mode,
+            # because a value of zero will make it wait forever, hanging the
+            # connection. Just return success instead, with empty content.
+            if clen == 0 then
+              @content = ""
+              @conn.pop_request
+              succeed(self)
+            else
+              @conn.set_text_mode clen
+            end
+          elsif chunks
+            @chunking = true
+          else
+            # Chunked transfer, multipart, or end-of-connection.
+            # For end-of-connection, we need to go the unbind
+            # method and suppress its desire to fail us.
+            p "NO CLEN"
+            p @args[:uri]
+            p @header_lines
+            @internal_error = :unsupported_clen
+            @conn.close_connection
+          end
+        end
+        private :process_header
+
+
+        def receive_text text
+          @chunking ? receive_chunked_text(text) : receive_sized_text(text)
+        end
+
+        #--
+        # At the present time, we only handle contents that have a length
+        # specified by the content-length header.
+        #
+        def receive_sized_text text
+          @content = text
+          @conn.pop_request
+          succeed(self)
+        end
+      end
+
+      # Make a connection to a remote HTTP server.
+      # Can take either a pair of arguments (which will be interpreted as
+      # a hostname/ip-address and a port), or a hash.
+      # If the arguments are a hash, then supported values include:
+      #  :host => a hostname or ip-address
+      #  :port => a port number
+      #  :ssl => true to enable ssl
+      def self.connect *args
+        if args.length == 2
+          args = {:host=>args[0], :port=>args[1]}
+        else
+          args = args.first
+        end
+
+        h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
+        conn = EM.connect( h, prt, self )
+        conn.start_tls if ssl
+        conn.set_default_host_header( h, prt, ssl )
+        conn
+      end
+
+      # Get a url
+      #
+      #  req = conn.get(:uri => '/')
+      #  req.callback{|response| puts response.content }
+      #
+      def get args
+        if args.is_a?(String)
+          args = {:uri=>args}
+        end
+        args[:verb] = "GET"
+        request args
+      end
+
+      # Post to a url
+      #
+      #  req = conn.post('/data')
+      #  req.callback{|response| puts response.content }
+      #--
+      # XXX there's no way to supply a POST body.. wtf?
+      def post args
+        if args.is_a?(String)
+          args = {:uri=>args}
+        end
+        args[:verb] = "POST"
+        request args
+      end
+
+      # :stopdoc:
+
+      #--
+      # Compute and remember a string to be used as the host header in HTTP requests
+      # unless the user overrides it with an argument to #request.
+      #
+      def set_default_host_header host, port, ssl
+        if (ssl and port != 443) or (!ssl and port != 80)
+          @host_header = "#{host}:#{port}"
+        else
+          @host_header = host
+        end
+      end
+
+
+      def post_init
+        super
+        @connected = EM::DefaultDeferrable.new
+      end
+
+      def connection_completed
+        super
+        @connected.succeed
+      end
+
+      #--
+      # All pending requests, if any, must fail.
+      # We might come here without ever passing through connection_completed
+      # in case we can't connect to the server. We'll also get here when the
+      # connection closes (either because the server closes it, or we close it
+      # due to detecting an internal error or security violation).
+      # In either case, run down all pending requests, if any, and signal failure
+      # on them.
+      #
+      # Set and remember a flag (@closed) so we can immediately fail any
+      # subsequent requests.
+      #
+      def unbind
+        super
+        @closed = true
+        (@requests || []).each {|r| r.fail}
+      end
+
+      def request args
+        args[:host_header] = @host_header unless args.has_key?(:host_header)
+        args[:authorization] = @authorization unless args.has_key?(:authorization)
+        r = Request.new self, args
+        if @closed
+          r.fail
+        else
+          (@requests ||= []).unshift r
+          @connected.callback {r.send_request}
+        end
+        r
+      end
+
+      def receive_line ln
+        if req = @requests.last
+          req.receive_line ln
+        else
+          p "??????????"
+          p ln
+        end
+
+      end
+      def receive_binary_data text
+        @requests.last.receive_text text
+      end
+
+      #--
+      # Called by a Request object when it completes.
+      #
+      def pop_request
+        @requests.pop
+      end
+
+      # :startdoc:
+    end
+
+
+=begin
+  class HttpClient2x < Connection
+    include LineText2
+
+    # TODO: Make this behave appropriate in case a #connect fails.
+    # Currently, this produces no errors.
+
+    # Make a connection to a remote HTTP server.
+    # Can take either a pair of arguments (which will be interpreted as
+    # a hostname/ip-address and a port), or a hash.
+    # If the arguments are a hash, then supported values include:
+    #  :host => a hostname or ip-address;
+    #  :port => a port number
+    #--
+    # TODO, support optional encryption arguments like :ssl
+    def self.connect *args
+      if args.length == 2
+        args = {:host=>args[0], :port=>args[1]}
+      else
+        args = args.first
+      end
+
+      h,prt = args[:host],Integer(args[:port])
+      EM.connect( h, prt, self, h, prt )
+    end
+
+
+    #--
+    # Sugars a connection that makes a single request and then
+    # closes the connection. Matches the behavior and the arguments
+    # of the original implementation of class HttpClient.
+    #
+    # Intended primarily for back compatibility, but the idiom
+    # is probably useful so it's not deprecated.
+    # We return a Deferrable, as did the original implementation.
+    #
+    # Because we're improving the way we deal with errors and exceptions
+    # (specifically, HTTP response codes other than 2xx will trigger the
+    # errback rather than the callback), this may break some existing code.
+    #
+    def self.request args
+      c = connect args
+    end
+
+    #--
+    # Requests can be pipelined. When we get a request, add it to the
+    # front of a queue as an array. The last element of the @requests
+    # array is always the oldest request received. Each element of the
+    # @requests array is a two-element array consisting of a hash with
+    # the original caller's arguments, and an initially-empty Ostruct
+    # containing the data we retrieve from the server's response.
+    # Maintain the instance variable @current_response, which is the response
+    # of the oldest pending request. That's just to make other code a little
+    # easier. If the variable doesn't exist when we come here, we're
+    # obviously the first request being made on the connection.
+    #
+    # The reason for keeping this method private (and requiring use of the
+    # convenience methods #get, #post, #head, etc) is to avoid the small
+    # performance penalty of canonicalizing the verb.
+    #
+    def request args
+      d = EventMachine::DefaultDeferrable.new
+
+      if @closed
+        d.fail
+        return d
+      end
+
+      o = OpenStruct.new
+      o.deferrable = d
+      (@requests ||= []).unshift [args, o]
+      @current_response ||= @requests.last.last
+      @connected.callback {
+        az = args[:authorization] and az = "Authorization: #{az}\r\n"
+
+        r = [
+          "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
+          "Host: #{args[:host_header] || @host_header}\r\n",
+          az || "",
+          "\r\n"
+        ]
+        p r
+        send_data r.join
+      }
+      o.deferrable
+    end
+    private :request
+
+    def get args
+      if args.is_a?(String)
+        args = {:uri=>args}
+      end
+      args[:verb] = "GET"
+      request args
+    end
+
+    def initialize host, port
+      super
+      @host_header = "#{host}:#{port}"
+    end
+    def post_init
+      super
+      @connected = EM::DefaultDeferrable.new
+    end
+
+
+    def connection_completed
+      super
+      @connected.succeed
+    end
+
+    #--
+    # Make sure to throw away any leftover incoming data if we've
+    # been closed due to recognizing an error.
+    #
+    # Generate an internal error if we get an unreasonable number of
+    # header lines. It could be malicious.
+    #
+    def receive_line ln
+      p ln
+      return if @closed
+
+      if ln.length > 0
+        (@current_response.headers ||= []).push ln
+        abort_connection if @current_response.headers.length > 100
+      else
+        process_received_headers
+      end
+    end
+
+    #--
+    # We come here when we've seen all the headers for a particular request.
+    # What we do next depends on the response line (which should be the
+    # first line in the header set), and whether there is content to read.
+    # We may transition into a text-reading state to read content, or
+    # we may abort the connection, or we may go right back into parsing
+    # responses for the next response in the chain.
+    #
+    # We make an ASSUMPTION that the first line is an HTTP response.
+    # Anything else produces an error that aborts the connection.
+    # This may not be enough, because it may be that responses to pipelined
+    # requests will come with a blank-line delimiter.
+    #
+    # Any non-2xx response will be treated as a fatal error, and abort the
+    # connection. We will set up the status and other response parameters.
+    # TODO: we will want to properly support 1xx responses, which some versions
+    # of IIS copiously generate.
+    # TODO: We need to give the option of not aborting the connection with certain
+    # non-200 responses, in order to work with NTLM and other authentication
+    # schemes that work at the level of individual connections.
+    #
+    # Some error responses will get sugarings. For example, we'll return the
+    # Location header in the response in case of a 301/302 response.
+    #
+    # Possible dispositions here:
+    # 1) No content to read (either content-length is zero or it's a HEAD request);
+    # 2) Switch to text mode to read a specific number of bytes;
+    # 3) Read a chunked or multipart response;
+    # 4) Read till the server closes the connection.
+    #
+    # Our reponse to the client can be either to wait till all the content
+    # has been read and then to signal caller's deferrable, or else to signal
+    # it when we finish the processing the headers and then expect the caller
+    # to have given us a block to call as the content comes in. And of course
+    # the latter gets stickier with chunks and multiparts.
+    #
+    HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+    ClenRE = /\AContent-length:\s*(\d+)/i
+    def process_received_headers
+      abort_connection unless @current_response.headers.first =~ HttpResponseRE
+      @current_response.version = $1.dup
+      st = $2.dup
+      @current_response.status = st.to_i
+      abort_connection unless st[0,1] == "2"
+
+      clen = nil
+      @current_response.headers.each do |e|
+        if clen == nil and e =~ ClenRE
+          clen = $1.dup.to_i
+        end
+      end
+
+      if clen
+        set_text_mode clen
+      end
+    end
+    private :process_received_headers
+
+
+    def receive_binary_data text
+      @current_response.content = text
+      @current_response.deferrable.succeed @current_response
+      @requests.pop
+      @current_response = (@requests.last || []).last
+      set_line_mode
+    end
+
+
+
+    # We've received either a server error or an internal error.
+    # Close the connection and abort any pending requests.
+    #--
+    # When should we call close_connection? It will cause #unbind
+    # to be fired. Should the user expect to see #unbind before
+    # we call #receive_http_error, or the other way around?
+    #
+    # Set instance variable @closed. That's used to inhibit further
+    # processing of any inbound data after an error has been recognized.
+    #
+    # We shouldn't have to worry about any leftover outbound data,
+    # because we call close_connection (not close_connection_after_writing).
+    # That ensures that any pipelined requests received after an error
+    # DO NOT get streamed out to the server on this connection.
+    # Very important. TODO, write a unit-test to establish that behavior.
+    #
+    def abort_connection
+      close_connection
+      @closed = true
+      @current_response.deferrable.fail( @current_response )
+    end
+
+
+    #------------------------
+    # Below here are user-overridable methods.
+
+  end
+=end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/line_and_text.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/line_and_text.rb
new file mode 100644 (file)
index 0000000..7417278
--- /dev/null
@@ -0,0 +1,125 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 15 November 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+# 
+
+module EventMachine
+  module Protocols
+    # A protocol that handles line-oriented data with interspersed binary text.
+    #
+    # This version is optimized for performance. See EventMachine::Protocols::LineText2
+    # for a version which is optimized for correctness with regard to binary text blocks
+    # that can switch back to line mode.
+    class LineAndTextProtocol < Connection
+      MaxLineLength = 16*1024
+      MaxBinaryLength = 32*1024*1024
+
+      def initialize *args
+        super
+        lbp_init_line_state
+      end
+      def receive_data data
+        if @lbp_mode == :lines
+          begin
+            @lpb_buffer.extract(data).each do |line| 
+              receive_line(line.chomp) if respond_to?(:receive_line)
+            end
+          rescue Exception
+            receive_error('overlength line') if respond_to?(:receive_error)
+            close_connection
+            return
+          end
+        else
+          if @lbp_binary_limit > 0
+            wanted = @lbp_binary_limit - @lbp_binary_bytes_received
+            chunk = nil
+            if data.length > wanted
+              chunk = data.slice!(0...wanted)
+            else
+              chunk = data
+              data = ""
+            end
+            @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
+            @lbp_binary_bytes_received += chunk.length
+            if @lbp_binary_bytes_received == @lbp_binary_limit
+              receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
+              lbp_init_line_state
+            end
+            receive_data(data) if data.length > 0
+          else
+            receive_binary_data(data) if respond_to?(:receive_binary_data)
+            data = ""
+          end
+        end
+      end
+
+      def unbind
+        if @lbp_mode == :binary and @lbp_binary_limit > 0
+          if respond_to?(:receive_binary_data)
+            receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
+          end
+        end
+      end
+
+      # Set up to read the supplied number of binary bytes.
+      # This recycles all the data currently waiting in the line buffer, if any.
+      # If the limit is nil, then ALL subsequent data will be treated as binary
+      # data and passed to the upstream protocol handler as we receive it.
+      # If a limit is given, we'll hold the incoming binary data and not
+      # pass it upstream until we've seen it all, or until there is an unbind
+      # (in which case we'll pass up a partial).
+      # Specifying nil for the limit (the default) means there is no limit.
+      # Specifiyng zero for the limit will cause an immediate transition back to line mode.
+      #
+      def set_binary_mode size = nil
+        if @lbp_mode == :lines
+          if size == 0
+            receive_binary_data("") if respond_to?(:receive_binary_data)
+            # Do no more work here. Stay in line mode and keep consuming data.
+          else
+            @lbp_binary_limit = size.to_i # (nil will be stored as zero)
+            if @lbp_binary_limit > 0
+              raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
+              @lbp_binary_buffer = "\0" * @lbp_binary_limit
+              @lbp_binary_bytes_received = 0
+            end
+
+            @lbp_mode = :binary
+            receive_data @lpb_buffer.flush
+          end
+        else
+          raise "invalid operation"
+        end
+      end
+
+      #--
+      # For internal use, establish protocol baseline for handling lines.
+      def lbp_init_line_state
+        @lpb_buffer = BufferedTokenizer.new("\n", MaxLineLength)
+        @lbp_mode = :lines
+      end
+      private :lbp_init_line_state
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/linetext2.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/linetext2.rb
new file mode 100644 (file)
index 0000000..6d4a47f
--- /dev/null
@@ -0,0 +1,161 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 15 November 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+module EventMachine
+  module Protocols
+    # In the grand, time-honored tradition of re-inventing the wheel, we offer
+    # here YET ANOTHER protocol that handles line-oriented data with interspersed
+    # binary text. This one trades away some of the performance optimizations of
+    # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
+    # with regard to binary text blocks that can switch back to line mode. It also
+    # permits the line-delimiter to change in midstream.
+    # This was originally written to support Stomp.
+    module LineText2
+      # TODO! We're not enforcing the limits on header lengths and text-lengths.
+      # When we get around to that, call #receive_error if the user defined it, otherwise
+      # throw exceptions.
+
+      MaxLineLength = 16*1024
+      MaxBinaryLength = 32*1024*1024
+
+      #--
+      # Will be called recursively until there's no data to read.
+      # That way the user-defined handlers we call can modify the
+      # handling characteristics on a per-token basis.
+      #
+      def receive_data data
+        return unless (data and data.length > 0)
+
+        # Do this stuff in lieu of a constructor.
+        @lt2_mode ||= :lines
+        @lt2_delimiter ||= "\n"
+        @lt2_linebuffer ||= []
+
+        if @lt2_mode == :lines
+          if ix = data.index( @lt2_delimiter )
+            @lt2_linebuffer << data[0...ix]
+            ln = @lt2_linebuffer.join
+            @lt2_linebuffer.clear
+            if @lt2_delimiter == "\n"
+              ln.chomp!
+            end
+            receive_line ln
+            receive_data data[(ix+@lt2_delimiter.length)..-1]
+          else
+            @lt2_linebuffer << data
+          end
+        elsif @lt2_mode == :text
+          if @lt2_textsize
+            needed = @lt2_textsize - @lt2_textpos
+            will_take = if data.length > needed
+                          needed
+                        else
+                          data.length
+                        end
+
+            @lt2_textbuffer << data[0...will_take]
+            tail = data[will_take..-1]
+
+            @lt2_textpos += will_take
+            if @lt2_textpos >= @lt2_textsize
+              # Reset line mode (the default behavior) BEFORE calling the
+              # receive_binary_data. This makes it possible for user code
+              # to call set_text_mode, enabling chains of text blocks
+              # (which can possibly be of different sizes).
+              set_line_mode
+              receive_binary_data @lt2_textbuffer.join
+              receive_end_of_binary_data
+            end
+
+            receive_data tail
+          else
+            receive_binary_data data
+          end
+        end
+      end
+
+
+      def set_delimiter delim
+        @lt2_delimiter = delim.to_s
+      end
+
+      # Called internally but also exposed to user code, for the case in which
+      # processing of binary data creates a need to transition back to line mode.
+      # We support an optional parameter to "throw back" some data, which might
+      # be an umprocessed chunk of the transmitted binary data, or something else
+      # entirely.
+      def set_line_mode data=""
+        @lt2_mode = :lines
+        (@lt2_linebuffer ||= []).clear
+        receive_data data.to_s
+      end
+
+      def set_text_mode size=nil
+        if size == 0
+          set_line_mode
+        else
+          @lt2_mode = :text
+          (@lt2_textbuffer ||= []).clear
+          @lt2_textsize = size # which can be nil, signifying no limit
+          @lt2_textpos = 0
+        end
+      end
+
+      # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
+      def set_binary_mode size=nil
+        set_text_mode size
+      end
+
+      # In case of a dropped connection, we'll send a partial buffer to user code
+      # when in sized text mode. User overrides of #receive_binary_data need to
+      # be aware that they may get a short buffer.
+      def unbind
+        @lt2_mode ||= nil
+        if @lt2_mode == :text and @lt2_textpos > 0
+          receive_binary_data @lt2_textbuffer.join
+        end
+      end
+
+      # Stub. Should be subclassed by user code.
+      def receive_line ln
+        # no-op
+      end
+
+      # Stub. Should be subclassed by user code.
+      def receive_binary_data data
+        # no-op
+      end
+
+      # Stub. Should be subclassed by user code.
+      # This is called when transitioning internally from text mode
+      # back to line mode. Useful when client code doesn't want
+      # to keep track of how much data it's received.
+      def receive_end_of_binary_data
+        # no-op
+      end
+    end
+  end
+end
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/memcache.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/memcache.rb
new file mode 100644 (file)
index 0000000..3132883
--- /dev/null
@@ -0,0 +1,323 @@
+module EventMachine\r
+  module Protocols\r
+    # Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).\r
+    # Requires memcached >= 1.2.4 w/ noreply support\r
+    #\r
+    # == Usage example\r
+    #\r
+    #   EM.run{\r
+    #     cache = EM::P::Memcache.connect 'localhost', 11211\r
+    #\r
+    #     cache.set :a, 'hello'\r
+    #     cache.set :b, 'hi'\r
+    #     cache.set :c, 'how are you?'\r
+    #     cache.set :d, ''\r
+    #\r
+    #     cache.get(:a){ |v| p v }\r
+    #     cache.get_hash(:a, :b, :c, :d){ |v| p v }\r
+    #     cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }\r
+    #\r
+    #     cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }\r
+    #\r
+    #     cache.get(:missing){ |m| p [:missing=, m] }\r
+    #     cache.set(:missing, 'abc'){ p :stored }\r
+    #     cache.get(:missing){ |m| p [:missing=, m] }\r
+    #     cache.del(:missing){ p :deleted }\r
+    #     cache.get(:missing){ |m| p [:missing=, m] }\r
+    #   }\r
+    #\r
+    module Memcache\r
+      include EM::Deferrable\r
+\r
+      ##\r
+      # constants\r
+\r
+      # :stopdoc:\r
+      unless defined? Cempty\r
+        Cstored    = 'STORED'.freeze\r
+        Cend       = 'END'.freeze\r
+        Cdeleted   = 'DELETED'.freeze\r
+        Cunknown   = 'NOT_FOUND'.freeze\r
+        Cerror     = 'ERROR'.freeze\r
+\r
+        Cempty     = ''.freeze\r
+        Cdelimiter = "\r\n".freeze\r
+      end\r
+      # :startdoc:\r
+\r
+      ##\r
+      # commands\r
+\r
+      # Get the value associated with one or multiple keys\r
+      #\r
+      #  cache.get(:a){ |v| p v }\r
+      #  cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }\r
+      #\r
+      def get *keys\r
+        raise ArgumentError unless block_given?\r
+\r
+        callback{\r
+          keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }\r
+          send_data "get #{keys.join(' ')}\r\n"\r
+          @get_cbs << [keys, proc{ |values|\r
+            yield *keys.map{ |k| values[k] }\r
+          }]\r
+        }\r
+      end\r
+\r
+      # Set the value for a given key\r
+      #\r
+      #  cache.set :a, 'hello'\r
+      #  cache.set(:missing, 'abc'){ puts "stored the value!" }\r
+      #\r
+      def set key, val, exptime = 0, &cb\r
+        callback{\r
+          val = val.to_s\r
+          send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?\r
+          send_data val\r
+          send_data Cdelimiter\r
+          @set_cbs << cb if cb\r
+        }\r
+      end\r
+\r
+      # Gets multiple values as a hash\r
+      #\r
+      #  cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }\r
+      #\r
+      def get_hash *keys\r
+        raise ArgumentError unless block_given?\r
+\r
+        get *keys do |*values|\r
+          yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }\r
+        end\r
+      end\r
+\r
+      # Delete the value associated with a key\r
+      #\r
+      #  cache.del :a\r
+      #  cache.del(:b){ puts "deleted the value!" }\r
+      #\r
+      def delete key, expires = 0, &cb\r
+        callback{\r
+          send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"\r
+          @del_cbs << cb if cb\r
+        }\r
+      end\r
+      alias del delete\r
+\r
+      # Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)\r
+      def self.connect host = 'localhost', port = 11211\r
+        EM.connect host, port, self, host, port\r
+      end\r
+\r
+      # :stopdoc:\r
+\r
+      def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false # :nodoc:\r
+        send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"\r
+      end\r
+      private :send_cmd\r
+\r
+      ##\r
+      # errors\r
+\r
+      class ParserError < StandardError\r
+      end\r
+\r
+      ##\r
+      # em hooks\r
+\r
+      def initialize host, port = 11211\r
+        @host, @port = host, port\r
+      end\r
+\r
+      def connection_completed\r
+        @get_cbs = []\r
+        @set_cbs = []\r
+        @del_cbs = []\r
+\r
+        @values = {}\r
+\r
+        @reconnecting = false\r
+        @connected = true\r
+        succeed\r
+        # set_delimiter "\r\n"\r
+        # set_line_mode\r
+      end\r
+\r
+      #--\r
+      # 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause\r
+      #         stack overflows when there is too much data.\r
+      # include EM::P::LineText2\r
+      def receive_data data\r
+        (@buffer||='') << data\r
+\r
+        while index = @buffer.index(Cdelimiter)\r
+          begin\r
+            line = @buffer.slice!(0,index+2)\r
+            process_cmd line\r
+          rescue ParserError\r
+            @buffer[0...0] = line\r
+            break\r
+          end\r
+        end\r
+      end\r
+\r
+      #--\r
+      # def receive_line line\r
+      def process_cmd line\r
+        case line.strip\r
+        when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>\r
+          bytes = Integer($3)\r
+          # set_binary_mode bytes+2\r
+          # @cur_key = $1\r
+          if @buffer.size >= bytes + 2\r
+            @values[$1] = @buffer.slice!(0,bytes)\r
+            @buffer.slice!(0,2) # \r\n\r
+          else\r
+            raise ParserError\r
+          end\r
+\r
+        when Cend # END\r
+          if entry = @get_cbs.shift\r
+            keys, cb = entry\r
+            cb.call(@values)\r
+          end\r
+          @values = {}\r
+\r
+        when Cstored # STORED\r
+          if cb = @set_cbs.shift\r
+            cb.call(true)\r
+          end\r
+\r
+        when Cdeleted # DELETED\r
+          if cb = @del_cbs.shift\r
+            cb.call(true)\r
+          end\r
+\r
+        when Cunknown # NOT_FOUND\r
+          if cb = @del_cbs.shift\r
+            cb.call(false)\r
+          end\r
+\r
+        else\r
+          p [:MEMCACHE_UNKNOWN, line]\r
+        end\r
+      end\r
+\r
+      #--\r
+      # def receive_binary_data data\r
+      #   @values[@cur_key] = data[0..-3]\r
+      # end\r
+\r
+      def unbind\r
+        if @connected or @reconnecting\r
+          EM.add_timer(1){ reconnect @host, @port }\r
+          @connected = false\r
+          @reconnecting = true\r
+          @deferred_status = nil\r
+        else\r
+          raise 'Unable to connect to memcached server'\r
+        end\r
+      end\r
+\r
+      # :startdoc:\r
+    end\r
+  end\r
+end\r
+\r
+if __FILE__ == $0\r
+  # ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb\r
+  require 'em/spec'\r
+\r
+  class TestConnection # :nodoc:\r
+    include EM::P::Memcache\r
+    def send_data data\r
+      sent_data << data\r
+    end\r
+    def sent_data\r
+      @sent_data ||= ''\r
+    end\r
+\r
+    def initialize\r
+      connection_completed\r
+    end\r
+  end\r
+\r
+  EM.describe EM::Protocols::Memcache do\r
+\r
+    before{\r
+      @c = TestConnection.new\r
+    }\r
+\r
+    should 'send get requests' do\r
+      @c.get('a'){}\r
+      @c.sent_data.should == "get a\r\n"\r
+      done\r
+    end\r
+\r
+    should 'send set requests' do\r
+      @c.set('a', 1){}\r
+      @c.sent_data.should == "set a 0 0 1\r\n1\r\n"\r
+      done\r
+    end\r
+\r
+    should 'use noreply on set without block' do\r
+      @c.set('a', 1)\r
+      @c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"\r
+      done\r
+    end\r
+\r
+    should 'send delete requests' do\r
+      @c.del('a')\r
+      @c.sent_data.should == "delete a 0 noreply\r\n"\r
+      done\r
+    end\r
+\r
+    should 'work when get returns no values' do\r
+      @c.get('a'){ |a|\r
+        a.should.be.nil\r
+        done\r
+      }\r
+\r
+      @c.receive_data "END\r\n"\r
+    end\r
+\r
+    should 'invoke block on set' do\r
+      @c.set('a', 1){\r
+        done\r
+      }\r
+\r
+      @c.receive_data "STORED\r\n"\r
+    end\r
+\r
+    should 'invoke block on delete' do\r
+      @c.delete('a'){ |found|\r
+        found.should.be.false\r
+      }\r
+      @c.delete('b'){ |found|\r
+        found.should.be.true\r
+        done\r
+      }\r
+\r
+      @c.receive_data "NOT_FOUND\r\n"\r
+      @c.receive_data "DELETED\r\n"\r
+    end\r
+\r
+    should 'parse split responses' do\r
+      @c.get('a'){ |a|\r
+        a.should == 'abc'\r
+        done\r
+      }\r
+\r
+      @c.receive_data "VAL"\r
+      @c.receive_data "UE a 0 "\r
+      @c.receive_data "3\r\n"\r
+      @c.receive_data "ab"\r
+      @c.receive_data "c"\r
+      @c.receive_data "\r\n"\r
+      @c.receive_data "EN"\r
+      @c.receive_data "D\r\n"\r
+    end\r
+\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/object_protocol.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/object_protocol.rb
new file mode 100644 (file)
index 0000000..d997fe9
--- /dev/null
@@ -0,0 +1,45 @@
+module EventMachine
+  module Protocols
+    # ObjectProtocol allows for easy communication using marshaled ruby objects
+    #
+    #  module RubyServer
+    #    include EM::P::ObjectProtocol
+    #
+    #    def receive_object obj
+    #      send_object({'you said' => obj})
+    #    end
+    #  end
+    #
+    module ObjectProtocol
+      # By default returns Marshal, override to return JSON or YAML, or any
+      # other serializer/deserializer responding to #dump and #load.
+      def serializer
+        Marshal
+      end
+
+      def receive_data data # :nodoc:
+        (@buf ||= '') << data
+
+        while @buf.size >= 4
+          if @buf.size >= 4+(size=@buf.unpack('N').first)
+            @buf.slice!(0,4)
+            receive_object serializer.load(@buf.slice!(0,size))
+          else
+            break
+          end
+        end
+      end
+
+      # Invoked with ruby objects received over the network
+      def receive_object obj
+        # stub
+      end
+
+      # Sends a ruby object over the network
+      def send_object obj
+        data = serializer.dump(obj)
+        send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*')
+      end
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/postgres3.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/postgres3.rb
new file mode 100644 (file)
index 0000000..acb2f9c
--- /dev/null
@@ -0,0 +1,247 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 15 November 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+# \r
+\r
+require 'readbytes'\r
+require 'postgres-pr/message'\r
+require 'postgres-pr/connection'\r
+require 'stringio'\r
+\r
+class StringIO # :nodoc:\r
+  # Reads exactly +n+ bytes.\r
+  #\r
+  # If the data read is nil an EOFError is raised.\r
+  #\r
+  # If the data read is too short a TruncatedDataError is raised and the read\r
+  # data is obtainable via its #data method.\r
+  def readbytes(n)\r
+    str = read(n)\r
+    if str == nil\r
+      raise EOFError, "End of file reached"\r
+    end\r
+    if str.size < n\r
+      raise TruncatedDataError.new("data truncated", str) \r
+    end\r
+    str\r
+  end\r
+  alias read_exactly_n_bytes readbytes\r
+end\r
+\r
+\r
+module EventMachine\r
+  module Protocols\r
+    # PROVISIONAL IMPLEMENTATION of an evented Postgres client.\r
+    # This implements version 3 of the Postgres wire protocol, which will work\r
+    # with any Postgres version from roughly 7.4 onward.\r
+    #\r
+    # Objective: we want to access Postgres databases without requiring threads.\r
+    # Until now this has been a problem because the Postgres client implementations\r
+    # have all made use of blocking I/O calls, which is incompatible with a\r
+    # thread-free evented model.\r
+    #\r
+    # But rather than re-implement the Postgres Wire3 protocol, we're taking advantage\r
+    # of the existing postgres-pr library, which was originally written by Michael\r
+    # Neumann but (at this writing) appears to be no longer maintained. Still, it's\r
+    # in basically a production-ready state, and the wire protocol isn't that complicated\r
+    # anyway.\r
+    #\r
+    # We need to monkeypatch StringIO because it lacks the #readbytes method needed\r
+    # by postgres-pr.\r
+    #\r
+    # We're tucking in a bunch of require statements that may not be present in garden-variety\r
+    # EM installations. Until we find a good way to only require these if a program\r
+    # requires postgres, this file will need to be required explicitly.\r
+    #\r
+    # The StringIO monkeypatch is lifted verbatim from the standard library readbytes.rb,\r
+    # which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.\r
+    #\r
+    # We cloned the handling of postgres messages from lib/postgres-pr/connection.rb\r
+    # in the postgres-pr library, and modified it for event-handling.\r
+    #\r
+    # TODO: The password handling in dispatch_conn_message is totally incomplete.\r
+    #\r
+    #\r
+    # We return Deferrables from the user-level operations surfaced by this interface.\r
+    # Experimentally, we're using the pattern of always returning a boolean value as the\r
+    # first argument of a deferrable callback to indicate success or failure. This is\r
+    # instead of the traditional pattern of calling Deferrable#succeed or #fail, and\r
+    # requiring the user to define both a callback and an errback function.\r
+    #\r
+    # === Usage\r
+    #  EM.run {\r
+    #    db = EM.connect_unix_domain( "/tmp/.s.PGSQL.5432", EM::P::Postgres3 )\r
+    #    db.connect( dbname, username, psw ).callback do |status|\r
+    #      if status\r
+    #        db.query( "select * from some_table" ).callback do |status, result, errors|\r
+    #          if status\r
+    #            result.rows.each do |row|\r
+    #              p row\r
+    #            end\r
+    #          end\r
+    #        end\r
+    #      end\r
+    #    end\r
+    #  }\r
+    class Postgres3 < EventMachine::Connection\r
+      include PostgresPR\r
+\r
+      def initialize\r
+        @data = ""\r
+        @params = {}\r
+      end\r
+\r
+      def connect db, user, psw=nil\r
+        d = EM::DefaultDeferrable.new\r
+        d.timeout 15\r
+\r
+        if @pending_query || @pending_conn\r
+          d.succeed false, "Operation already in progress"\r
+        else\r
+          @pending_conn = d\r
+          prms = {"user"=>user, "database"=>db}\r
+          @user = user\r
+          if psw\r
+            @password = psw\r
+            #prms["password"] = psw\r
+          end\r
+          send_data PostgresPR::StartupMessage.new( 3 << 16, prms ).dump\r
+        end\r
+\r
+        d\r
+      end\r
+\r
+      def query sql\r
+        d = EM::DefaultDeferrable.new\r
+        d.timeout 15\r
+\r
+        if @pending_query || @pending_conn\r
+          d.succeed false, "Operation already in progress"\r
+        else\r
+          @r = PostgresPR::Connection::Result.new\r
+          @e = []\r
+          @pending_query = d\r
+          send_data PostgresPR::Query.dump(sql)\r
+        end\r
+\r
+        d\r
+      end\r
+\r
+\r
+      def receive_data data\r
+        @data << data\r
+        while @data.length >= 5\r
+          pktlen = @data[1...5].unpack("N").first\r
+          if @data.length >= (1 + pktlen)\r
+            pkt = @data.slice!(0...(1+pktlen))\r
+            m = StringIO.open( pkt, "r" ) {|io| PostgresPR::Message.read( io ) }\r
+            if @pending_conn\r
+              dispatch_conn_message m\r
+            elsif @pending_query\r
+              dispatch_query_message m\r
+            else\r
+              raise "Unexpected message from database"\r
+            end\r
+          else\r
+            break # very important, break out of the while\r
+          end\r
+        end\r
+      end\r
+\r
+\r
+      def unbind\r
+        if o = (@pending_query || @pending_conn)\r
+          o.succeed false, "lost connection"\r
+        end\r
+      end\r
+\r
+      # Cloned and modified from the postgres-pr.\r
+      def dispatch_conn_message msg\r
+        case msg\r
+        when AuthentificationClearTextPassword\r
+          raise ArgumentError, "no password specified" if @password.nil?\r
+          send_data PasswordMessage.new(@password).dump\r
+\r
+        when AuthentificationCryptPassword\r
+          raise ArgumentError, "no password specified" if @password.nil?\r
+          send_data PasswordMessage.new(@password.crypt(msg.salt)).dump\r
+\r
+        when AuthentificationMD5Password\r
+          raise ArgumentError, "no password specified" if @password.nil?\r
+          require 'digest/md5'\r
+\r
+          m = Digest::MD5.hexdigest(@password + @user)\r
+          m = Digest::MD5.hexdigest(m + msg.salt)\r
+          m = 'md5' + m\r
+          send_data PasswordMessage.new(m).dump\r
+\r
+        when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential\r
+          raise "unsupported authentification"\r
+\r
+        when AuthentificationOk\r
+        when ErrorResponse\r
+          raise msg.field_values.join("\t")\r
+        when NoticeResponse\r
+          @notice_processor.call(msg) if @notice_processor\r
+        when ParameterStatus\r
+          @params[msg.key] = msg.value\r
+        when BackendKeyData\r
+          # TODO\r
+          #p msg\r
+        when ReadyForQuery\r
+          # TODO: use transaction status\r
+          pc,@pending_conn = @pending_conn,nil\r
+          pc.succeed true\r
+        else\r
+          raise "unhandled message type"\r
+        end\r
+      end\r
+\r
+      # Cloned and modified from the postgres-pr.\r
+      def dispatch_query_message msg\r
+        case msg\r
+        when DataRow\r
+          @r.rows << msg.columns\r
+        when CommandComplete\r
+          @r.cmd_tag = msg.cmd_tag\r
+        when ReadyForQuery\r
+          pq,@pending_query = @pending_query,nil\r
+          pq.succeed true, @r, @e\r
+        when RowDescription\r
+          @r.fields = msg.fields\r
+        when CopyInResponse\r
+        when CopyOutResponse\r
+        when EmptyQueryResponse\r
+        when ErrorResponse\r
+          # TODO\r
+          @e << msg\r
+        when NoticeResponse\r
+          @notice_processor.call(msg) if @notice_processor\r
+        else\r
+          # TODO\r
+        end\r
+      end\r
+    end\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/saslauth.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/saslauth.rb
new file mode 100644 (file)
index 0000000..f17deaa
--- /dev/null
@@ -0,0 +1,175 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 15 November 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+# \r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    # Implements SASL authd.\r
+    # This is a very, very simple protocol that mimics the one used\r
+    # by saslauthd and pwcheck, two outboard daemons included in the\r
+    # standard SASL library distro.\r
+    # The only thing this is really suitable for is SASL PLAIN\r
+    # (user+password) authentication, but the SASL libs that are\r
+    # linked into standard servers (like imapd and sendmail) implement\r
+    # the other ones.\r
+    #\r
+    # SASL-auth is intended for reasonably fast operation inside a\r
+    # single machine, so it has no transport-security (although there\r
+    # have been multi-machine extensions incorporating transport-layer\r
+    # encryption).\r
+    #\r
+    # The standard saslauthd module generally runs privileged and does\r
+    # its work by referring to the system-account files.\r
+    #\r
+    # This feature was added to EventMachine to enable the development\r
+    # of custom authentication/authorization engines for standard servers.\r
+    #\r
+    # To use SASLauth, include it in a class that subclasses EM::Connection,\r
+    # and reimplement the validate method.\r
+    #\r
+    # The typical way to incorporate this module into an authentication\r
+    # daemon would be to set it as the handler for a UNIX-domain socket.\r
+    # The code might look like this:\r
+    #\r
+    #  EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )\r
+    #  File.chmod( 0777, "/var/run/saslauthd/mux")\r
+    #\r
+    # The chmod is probably needed to ensure that unprivileged clients can\r
+    # access the UNIX-domain socket.\r
+    #\r
+    # It's also a very good idea to drop superuser privileges (if any), after\r
+    # the UNIX-domain socket has been opened.\r
+    #--\r
+    # Implementation details: assume the client can send us pipelined requests,\r
+    # and that the client will close the connection.\r
+    #\r
+    # The client sends us four values, each encoded as a two-byte length field in\r
+    # network order followed by the specified number of octets.\r
+    # The fields specify the username, password, service name (such as imap),\r
+    # and the "realm" name. We send back the barest minimum reply, a single\r
+    # field also encoded as a two-octet length in network order, followed by\r
+    # either "NO" or "OK" - simplicity itself.\r
+    #\r
+    # We enforce a maximum field size just as a sanity check.\r
+    # We do NOT automatically time out the connection.\r
+    #\r
+    # The code we use to parse out the values is ugly and probably slow.\r
+    # Improvements welcome.\r
+    #\r
+    module SASLauth\r
+\r
+      MaxFieldSize = 128*1024\r
+      def post_init\r
+        super\r
+        @sasl_data = ""\r
+        @sasl_values = []\r
+      end\r
+\r
+      def receive_data data\r
+        @sasl_data << data\r
+        while @sasl_data.length >= 2\r
+          len = (@sasl_data[0,2].unpack("n")).first\r
+          raise "SASL Max Field Length exceeded" if len > MaxFieldSize\r
+          if @sasl_data.length >= (len + 2)\r
+            @sasl_values << @sasl_data[2,len]\r
+            @sasl_data.slice!(0...(2+len))\r
+            if @sasl_values.length == 4\r
+              send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )\r
+              @sasl_values.clear\r
+            end\r
+          else\r
+            break\r
+          end\r
+        end\r
+      end\r
+\r
+      def validate username, psw, sysname, realm\r
+        p username\r
+        p psw\r
+        p sysname\r
+        p realm\r
+        true\r
+      end\r
+    end\r
+\r
+    # Implements the SASL authd client protocol.\r
+    # This is a very, very simple protocol that mimics the one used\r
+    # by saslauthd and pwcheck, two outboard daemons included in the\r
+    # standard SASL library distro.\r
+    # The only thing this is really suitable for is SASL PLAIN\r
+    # (user+password) authentication, but the SASL libs that are\r
+    # linked into standard servers (like imapd and sendmail) implement\r
+    # the other ones.\r
+    #\r
+    # You can use this module directly as a handler for EM Connections,\r
+    # or include it in a module or handler class of your own.\r
+    #\r
+    # First connect to a SASL server (it's probably a TCP server, or more\r
+    # likely a Unix-domain socket). Then call the #validate? method,\r
+    # passing at least a username and a password. #validate? returns\r
+    # a Deferrable which will either succeed or fail, depending\r
+    # on the status of the authentication operation.\r
+    #\r
+    module SASLauthclient\r
+      MaxFieldSize = 128*1024\r
+\r
+      def validate? username, psw, sysname=nil, realm=nil\r
+\r
+        str = [username, psw, sysname, realm].map {|m|\r
+          [(m || "").length, (m || "")]\r
+        }.flatten.pack( "nA*" * 4 )\r
+        send_data str\r
+\r
+        d = EM::DefaultDeferrable.new\r
+        @queries.unshift d\r
+        d\r
+      end\r
+\r
+      def post_init\r
+        @sasl_data = ""\r
+        @queries = []\r
+      end\r
+\r
+      def receive_data data\r
+        @sasl_data << data\r
+\r
+        while @sasl_data.length > 2\r
+          len = (@sasl_data[0,2].unpack("n")).first\r
+          raise "SASL Max Field Length exceeded" if len > MaxFieldSize\r
+          if @sasl_data.length >= (len + 2)\r
+            val = @sasl_data[2,len]\r
+            @sasl_data.slice!(0...(2+len))\r
+            q = @queries.pop\r
+            (val == "NO") ? q.fail : q.succeed\r
+          else\r
+            break\r
+          end\r
+        end\r
+      end\r
+    end\r
+\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpclient.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpclient.rb
new file mode 100644 (file)
index 0000000..3bbcba6
--- /dev/null
@@ -0,0 +1,357 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 16 July 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+require 'ostruct'
+
+module EventMachine
+  module Protocols
+
+    # Simple SMTP client
+    #
+    #  email = EM::Protocols::SmtpClient.send(
+    #    :domain=>"example.com",
+    #    :host=>'localhost',
+    #    :port=>25, # optional, defaults 25
+    #    :starttls=>true, # use ssl
+    #    :from=>"sender@example.com",
+    #    :to=> ["to_1@example.com", "to_2@example.com"],
+    #    :header=> {"Subject" => "This is a subject line"},
+    #    :body=> "This is the body of the email"
+    #  )
+    #  email.callback{
+    #    puts 'Email sent!'
+    #  }
+    #  email.errback{ |e|
+    #    puts 'Email failed!'
+    #  }
+    #
+    # Sending generated emails (using mailfactory)
+    #
+    #  mail = MailFactory.new
+    #  mail.to = 'someone@site.co'
+    #  mail.from = 'me@site.com'
+    #  mail.subject = 'hi!'
+    #  mail.text = 'hello world'
+    #  mail.html = '<h1>hello world</h1>'
+    #
+    #  email = EM::P::SmtpClient.send(
+    #    :domain=>'site.com',
+    #    :from=>mail.from,
+    #    :to=>mail.to,
+    #    :content=>"#{mail.to_s}\r\n.\r\n"
+    #  )
+    #
+    class SmtpClient < Connection
+      include EventMachine::Deferrable
+      include EventMachine::Protocols::LineText2
+      
+      def initialize
+        @succeeded = nil
+        @responder = nil
+        @code = nil
+        @msg = nil
+      end
+
+      # :host => required String
+      #   a string containing the IP address or host name of the SMTP server to connect to.
+      # :port => optional
+      #   defaults to 25.
+      # :domain => required String
+      #   This is passed as the argument to the EHLO command.
+      # :starttls => optional Boolean
+      #   If it evaluates true, then the client will initiate STARTTLS with
+      #   the server, and abort the connection if the negotiation doesn't succeed.
+      #   TODO, need to be able to pass certificate parameters with this option.
+      # :auth => optional Hash of auth parameters
+      #   If not given, then no auth will be attempted.
+      #   (In that case, the connection will be aborted if the server requires auth.)
+      #   Specify the hash value :type to determine the auth type, along with additional parameters
+      #   depending on the type.
+      #   Currently only :type => :plain is supported. Pass additional parameters :username (String),
+      #   and :password (either a String or a Proc that will be called at auth-time).
+      #   Example: :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
+      # :from => required String
+      #   Specifies the sender of the message. Will be passed as the argument
+      #   to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
+      #   The connection will abort if the server rejects the value.
+      # :to => required String or Array of Strings
+      #   The recipient(s) of the message. Do NOT enclose
+      #   any of the values in angle-brackets (<>) characters. It's NOT a fatal error if one or more
+      #   recipients are rejected by the server. (Of course, if ALL of them are, the server will most
+      #   likely trigger an error when we try to send data.) An array of codes containing the status
+      #   of each requested recipient is available after the call completes. TODO, we should define
+      #   an overridable stub that will be called on rejection of a recipient or a sender, giving
+      #   user code the chance to try again or abort the connection.
+      # :header => Required hash of values to be transmitted in the header of the message.
+      #   The hash keys are the names of the headers (do NOT append a trailing colon), and the values are strings
+      #   containing the header values. TODO, support Arrays of header values, which would cause us to
+      #   send that specific header line more than once.
+      #
+      #   Example: :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
+      # :body => Optional string, defaults blank.
+      #   This will be passed as the body of the email message.
+      #   TODO, this needs to be significantly beefed up. As currently written, this requires the caller
+      #   to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
+      #   SMTP transmission format. We need to be able to automatically convert binary data, and add
+      #   correct line-breaks to text data. I think the :body parameter should remain as it is, and we
+      #   should add a :content parameter that contains autoconversions and/or conversion parameters.
+      #   Then we can check if either :body or :content is present and do the right thing.
+      # :content => Optional array or string
+      #   Alternative to providing header and body, an array or string of raw data which MUST be in
+      #   correct SMTP body format, including a trailing dot line
+      # :verbose => Optional.
+      #   If true, will cause a lot of information (including the server-side of the
+      #   conversation) to be dumped to $>.
+      #
+      def self.send args={}
+        args[:port] ||= 25
+        args[:body] ||= ""
+
+=begin
+      (I don't think it's possible for EM#connect to throw an exception under normal
+      circumstances, so this original code is stubbed out. A connect-failure will result
+      in the #unbind method being called without calling #connection_completed.)
+      begin
+        EventMachine.connect( args[:host], args[:port], self) {|c|
+          # According to the EM docs, we will get here AFTER post_init is called.
+          c.args = args
+          c.set_comm_inactivity_timeout 60
+        }
+      rescue
+        # We'll get here on a connect error. This code mimics the effect
+        # of a call to invoke_internal_error. Would be great to DRY this up.
+        # (Actually, it may be that we never get here, if EM#connect catches
+        # its errors internally.)
+        d = EM::DefaultDeferrable.new
+        d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
+        d
+      end
+=end
+        EventMachine.connect( args[:host], args[:port], self) {|c|
+          # According to the EM docs, we will get here AFTER post_init is called.
+          c.args = args
+          c.set_comm_inactivity_timeout 60
+        }
+      end
+
+      # :stopdoc:
+
+      attr_writer :args
+
+      def post_init
+        @return_values = OpenStruct.new
+        @return_values.start_time = Time.now
+      end
+
+      def connection_completed
+        @responder = :receive_signon
+        @msg = []
+      end
+
+      # We can get here in a variety of ways, all of them being failures unless
+      # the @succeeded flag is set. If a protocol success was recorded, then don't
+      # set a deferred success because the caller will already have done it
+      # (no need to wait until the connection closes to invoke the callbacks).
+      #
+      def unbind
+        unless @succeeded
+          @return_values.elapsed_time = Time.now - @return_values.start_time
+          @return_values.responder = @responder
+          @return_values.code = @code
+          @return_values.message = @msg
+          set_deferred_status(:failed, @return_values)
+        end
+      end
+
+      def receive_line ln
+        $>.puts ln if @args[:verbose]
+        @range = ln[0...1].to_i
+        @code = ln[0...3].to_i
+        @msg << ln[4..-1]
+        unless ln[3...4] == '-'
+          $>.puts @responder if @args[:verbose]
+          send @responder
+          @msg.clear
+        end
+      end
+
+      # We encountered an error from the server and will close the connection.
+      # Use the error and message the server returned.
+      #
+      def invoke_error
+        @return_values.elapsed_time = Time.now - @return_values.start_time
+        @return_values.responder = @responder
+        @return_values.code = @code
+        @return_values.message = @msg
+        set_deferred_status :failed, @return_values
+        send_data "QUIT\r\n"
+        close_connection_after_writing
+      end
+
+      # We encountered an error on our side of the protocol and will close the connection.
+      # Use an extra-protocol error code (900) and use the message from the caller.
+      #
+      def invoke_internal_error msg = "???"
+        @return_values.elapsed_time = Time.now - @return_values.start_time
+        @return_values.responder = @responder
+        @return_values.code = 900
+        @return_values.message = msg
+        set_deferred_status :failed, @return_values
+        send_data "QUIT\r\n"
+        close_connection_after_writing
+      end
+
+      def receive_signon
+        return invoke_error unless @range == 2
+        send_data "EHLO #{@args[:domain]}\r\n"
+        @responder = :receive_ehlo_response
+      end
+
+      def receive_ehlo_response
+        return invoke_error unless @range == 2
+        @server_caps = @msg
+        invoke_starttls
+      end
+
+      def invoke_starttls
+        if @args[:starttls]
+          # It would be more sociable to first ask if @server_caps contains
+          # the string "STARTTLS" before we invoke it, but hey, life's too short.
+          send_data "STARTTLS\r\n"
+          @responder = :receive_starttls_response
+        else
+          invoke_auth
+        end
+      end
+      def receive_starttls_response
+        return invoke_error unless @range == 2
+        start_tls
+        invoke_auth
+      end
+
+      # Perform an authentication. If the caller didn't request one, then fall through
+      # to the mail-from state.
+      def invoke_auth
+        if @args[:auth]
+          if @args[:auth][:type] == :plain
+            psw = @args[:auth][:password]
+            if psw.respond_to?(:call)
+              psw = psw.call
+            end
+            #str = Base64::encode64("\0#{@args[:auth][:username]}\0#{psw}").chomp
+            str = ["\0#{@args[:auth][:username]}\0#{psw}"].pack("m").chomp
+            send_data "AUTH PLAIN #{str}\r\n"
+            @responder = :receive_auth_response
+          else
+            return invoke_internal_error("unsupported auth type")
+          end
+        else
+          invoke_mail_from
+        end
+      end
+      def receive_auth_response
+        return invoke_error unless @range == 2
+        invoke_mail_from
+      end
+
+      def invoke_mail_from
+        send_data "MAIL FROM: <#{@args[:from]}>\r\n"
+        @responder = :receive_mail_from_response
+      end
+      def receive_mail_from_response
+        return invoke_error unless @range == 2
+        invoke_rcpt_to
+      end
+
+      def invoke_rcpt_to
+        @rcpt_responses ||= []
+        l = @rcpt_responses.length
+        to = @args[:to].is_a?(Array) ? @args[:to] : [@args[:to].to_s]
+        if l < to.length
+          send_data "RCPT TO: <#{to[l]}>\r\n"
+          @responder = :receive_rcpt_to_response
+        else
+          e = @rcpt_responses.select {|rr| rr.last == 2}
+          if e and e.length > 0
+            invoke_data
+          else
+            invoke_error
+          end
+        end
+      end
+      def receive_rcpt_to_response
+        @rcpt_responses << [@code, @msg, @range]
+        invoke_rcpt_to
+      end
+
+      def invoke_data
+        send_data "DATA\r\n"
+        @responder = :receive_data_response
+      end
+      def receive_data_response
+        return invoke_error unless @range == 3
+
+        # The data to send can be given either in @args[:content] (an array or string of raw data
+        # which MUST be in correct SMTP body format, including a trailing dot line), or a header and
+        # body given in @args[:header] and @args[:body].
+        #
+        if @args[:content]
+          send_data @args[:content].to_s
+        else
+          # The header can be a hash or an array.
+          if @args[:header].is_a?(Hash)
+            (@args[:header] || {}).each {|k,v| send_data "#{k}: #{v}\r\n" }
+          else
+            send_data @args[:header].to_s
+          end
+          send_data "\r\n"
+
+          if @args[:body].is_a?(Array)
+            @args[:body].each {|e| send_data e}
+          else
+            send_data @args[:body].to_s
+          end
+
+          send_data "\r\n.\r\n"
+        end
+
+        @responder = :receive_message_response
+      end
+      def receive_message_response
+        return invoke_error unless @range == 2
+        send_data "QUIT\r\n"
+        close_connection_after_writing
+        @succeeded = true
+        @return_values.elapsed_time = Time.now - @return_values.start_time
+        @return_values.responder = @responder
+        @return_values.code = @code
+        @return_values.message = @msg
+        set_deferred_status :succeeded, @return_values
+      end
+
+      # :startdoc:
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpserver.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/smtpserver.rb
new file mode 100644 (file)
index 0000000..16cf347
--- /dev/null
@@ -0,0 +1,547 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 July 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    # This is a protocol handler for the server side of SMTP.\r
+    # It's NOT a complete SMTP server obeying all the semantics of servers conforming to\r
+    # RFC2821. Rather, it uses overridable method stubs to communicate protocol states\r
+    # and data to user code. User code is responsible for doing the right things with the\r
+    # data in order to get complete and correct SMTP server behavior.\r
+    #\r
+    #--\r
+    # Useful paragraphs in RFC-2821:\r
+    # 4.3.2: Concise list of command-reply sequences, in essence a text representation\r
+    # of the command state-machine.\r
+    #\r
+    # STARTTLS is defined in RFC2487.\r
+    # Observe that there are important rules governing whether a publicly-referenced server\r
+    # (meaning one whose Internet address appears in public MX records) may require the\r
+    # non-optional use of TLS.\r
+    # Non-optional TLS does not apply to EHLO, NOOP, QUIT or STARTTLS.\r
+    class SmtpServer < EventMachine::Connection\r
+      include Protocols::LineText2\r
+\r
+      HeloRegex = /\AHELO\s*/i\r
+      EhloRegex = /\AEHLO\s*/i\r
+      QuitRegex = /\AQUIT/i\r
+      MailFromRegex = /\AMAIL FROM:\s*/i\r
+      RcptToRegex = /\ARCPT TO:\s*/i\r
+      DataRegex = /\ADATA/i\r
+      NoopRegex = /\ANOOP/i\r
+      RsetRegex = /\ARSET/i\r
+      VrfyRegex = /\AVRFY\s+/i\r
+      ExpnRegex = /\AEXPN\s+/i\r
+      HelpRegex = /\AHELP/i\r
+      StarttlsRegex = /\ASTARTTLS/i\r
+      AuthRegex = /\AAUTH\s+/i\r
+\r
+\r
+      # Class variable containing default parameters that can be overridden\r
+      # in application code.\r
+      # Individual objects of this class will make an instance-local copy of\r
+      # the class variable, so that they can be reconfigured on a per-instance\r
+      # basis.\r
+      #\r
+      # Chunksize is the number of data lines we'll buffer before\r
+      # sending them to the application. TODO, make this user-configurable.\r
+      #\r
+      @@parms = {\r
+        :chunksize => 4000,\r
+        :verbose => false\r
+      }\r
+      def self.parms= parms={}\r
+        @@parms.merge!(parms)\r
+      end\r
+\r
+\r
+\r
+      def initialize *args\r
+        super\r
+        @parms = @@parms\r
+        init_protocol_state\r
+      end\r
+\r
+      def parms= parms={}\r
+        @parms.merge!(parms)\r
+      end\r
+\r
+      # In SMTP, the server talks first. But by a (perhaps flawed) axiom in EM,\r
+      # #post_init will execute BEFORE the block passed to #start_server, for any\r
+      # given accepted connection. Since in this class we'll probably be getting\r
+      # a lot of initialization parameters, we want the guts of post_init to\r
+      # run AFTER the application has initialized the connection object. So we\r
+      # use a spawn to schedule the post_init to run later.\r
+      # It's a little weird, I admit. A reasonable alternative would be to set\r
+      # parameters as a class variable and to do that before accepting any connections.\r
+      #\r
+      # OBSOLETE, now we have @@parms. But the spawn is nice to keep as an illustration.\r
+      #\r
+      def post_init\r
+        #send_data "220 #{get_server_greeting}\r\n" (ORIGINAL)\r
+        #(EM.spawn {|x| x.send_data "220 #{x.get_server_greeting}\r\n"}).notify(self)\r
+        (EM.spawn {|x| x.send_server_greeting}).notify(self)\r
+      end\r
+\r
+      def send_server_greeting\r
+        send_data "220 #{get_server_greeting}\r\n"\r
+      end\r
+\r
+      def receive_line ln\r
+        @@parms[:verbose] and $>.puts ">>> #{ln}"\r
+\r
+        return process_data_line(ln) if @state.include?(:data)\r
+\r
+        case ln\r
+        when EhloRegex\r
+          process_ehlo $'.dup\r
+        when HeloRegex\r
+          process_helo $'.dup\r
+        when MailFromRegex\r
+          process_mail_from $'.dup\r
+        when RcptToRegex\r
+          process_rcpt_to $'.dup\r
+        when DataRegex\r
+          process_data\r
+        when RsetRegex\r
+          process_rset\r
+        when VrfyRegex\r
+          process_vrfy\r
+        when ExpnRegex\r
+          process_expn\r
+        when HelpRegex\r
+          process_help\r
+        when NoopRegex\r
+          process_noop\r
+        when QuitRegex\r
+          process_quit\r
+        when StarttlsRegex\r
+          process_starttls\r
+        when AuthRegex\r
+          process_auth $'.dup\r
+        else\r
+          process_unknown\r
+        end\r
+      end\r
+\r
+      # TODO - implement this properly, the implementation is a stub!\r
+      def process_vrfy\r
+        send_data "250 Ok, but unimplemented\r\n"\r
+      end\r
+      # TODO - implement this properly, the implementation is a stub!\r
+      def process_help\r
+        send_data "250 Ok, but unimplemented\r\n"\r
+      end\r
+      # TODO - implement this properly, the implementation is a stub!\r
+      def process_expn\r
+        send_data "250 Ok, but unimplemented\r\n"\r
+      end\r
+\r
+      #--\r
+      # This is called at several points to restore the protocol state\r
+      # to a pre-transaction state. In essence, we "forget" having seen\r
+      # any valid command except EHLO and STARTTLS.\r
+      # We also have to callback user code, in case they're keeping track\r
+      # of senders, recipients, and whatnot.\r
+      #\r
+      # We try to follow the convention of avoiding the verb "receive" for\r
+      # internal method names except receive_line (which we inherit), and\r
+      # using only receive_xxx for user-overridable stubs.\r
+      #\r
+      # init_protocol_state is called when we initialize the connection as\r
+      # well as during reset_protocol_state. It does NOT call the user\r
+      # override method. This enables us to promise the users that they\r
+      # won't see the overridable fire except after EHLO and RSET, and\r
+      # after a message has been received. Although the latter may be wrong.\r
+      # The standard may allow multiple DATA segments with the same set of\r
+      # senders and recipients.\r
+      #\r
+      def reset_protocol_state\r
+        init_protocol_state\r
+        s,@state = @state,[]\r
+        @state << :starttls if s.include?(:starttls)\r
+        @state << :ehlo if s.include?(:ehlo)\r
+        receive_transaction\r
+      end\r
+      def init_protocol_state\r
+        @state ||= []\r
+      end\r
+\r
+\r
+      #--\r
+      # EHLO/HELO is always legal, per the standard. On success\r
+      # it always clears buffers and initiates a mail "transaction."\r
+      # Which means that a MAIL FROM must follow.\r
+      #\r
+      # Per the standard, an EHLO/HELO or a RSET "initiates" an email\r
+      # transaction. Thereafter, MAIL FROM must be received before\r
+      # RCPT TO, before DATA. Not sure what this specific ordering\r
+      # achieves semantically, but it does make it easier to\r
+      # implement. We also support user-specified requirements for\r
+      # STARTTLS and AUTH. We make it impossible to proceed to MAIL FROM\r
+      # without fulfilling tls and/or auth, if the user specified either\r
+      # or both as required. We need to check the extension standard\r
+      # for auth to see if a credential is discarded after a RSET along\r
+      # with all the rest of the state. We'll behave as if it is.\r
+      # Now clearly, we can't discard tls after its been negotiated\r
+      # without dropping the connection, so that flag doesn't get cleared.\r
+      #\r
+      def process_ehlo domain\r
+        if receive_ehlo_domain domain\r
+          send_data "250-#{get_server_domain}\r\n"\r
+          if @@parms[:starttls]\r
+            send_data "250-STARTTLS\r\n"\r
+          end\r
+          if @@parms[:auth]\r
+            send_data "250-AUTH PLAIN LOGIN\r\n"\r
+          end\r
+          send_data "250-NO-SOLICITING\r\n"\r
+          # TODO, size needs to be configurable.\r
+          send_data "250 SIZE 20000000\r\n"\r
+          reset_protocol_state\r
+          @state << :ehlo\r
+        else\r
+          send_data "550 Requested action not taken\r\n"\r
+        end\r
+      end\r
+\r
+      def process_helo domain\r
+        if receive_ehlo_domain domain.dup\r
+          send_data "250 #{get_server_domain}\r\n"\r
+          reset_protocol_state\r
+          @state << :ehlo\r
+        else\r
+          send_data "550 Requested action not taken\r\n"\r
+        end\r
+      end\r
+\r
+      def process_quit\r
+        send_data "221 Ok\r\n"\r
+        close_connection_after_writing\r
+      end\r
+\r
+      def process_noop\r
+        send_data "250 Ok\r\n"\r
+      end\r
+\r
+      def process_unknown\r
+        send_data "500 Unknown command\r\n"\r
+      end\r
+\r
+      #--\r
+      # So far, only AUTH PLAIN is supported but we should do at least LOGIN as well.\r
+      # TODO, support clients that send AUTH PLAIN with no parameter, expecting a 3xx\r
+      # response and a continuation of the auth conversation.\r
+      #\r
+      def process_auth str\r
+        if @state.include?(:auth)\r
+          send_data "503 auth already issued\r\n"\r
+        elsif str =~ /\APLAIN\s+/i\r
+          plain = ($'.dup).unpack("m").first # Base64::decode64($'.dup)\r
+          discard,user,psw = plain.split("\000")\r
+          if receive_plain_auth user,psw\r
+            send_data "235 authentication ok\r\n"\r
+            @state << :auth\r
+          else\r
+            send_data "535 invalid authentication\r\n"\r
+          end\r
+          #elsif str =~ /\ALOGIN\s+/i\r
+        else\r
+          send_data "504 auth mechanism not available\r\n"\r
+        end\r
+      end\r
+\r
+      #--\r
+      # Unusually, we can deal with a Deferrable returned from the user application.\r
+      # This was added to deal with a special case in a particular application, but\r
+      # it would be a nice idea to add it to the other user-code callbacks.\r
+      #\r
+      def process_data\r
+        unless @state.include?(:rcpt)\r
+          send_data "503 Operation sequence error\r\n"\r
+        else\r
+          succeeded = proc {\r
+            send_data "354 Send it\r\n"\r
+            @state << :data\r
+            @databuffer = []\r
+          }\r
+          failed = proc {\r
+            send_data "550 Operation failed\r\n"\r
+          }\r
+\r
+          d = receive_data_command\r
+\r
+          if d.respond_to?(:callback)\r
+            d.callback(&succeeded)\r
+            d.errback(&failed)\r
+          else\r
+            (d ? succeeded : failed).call\r
+          end\r
+        end\r
+      end\r
+\r
+      def process_rset\r
+        reset_protocol_state\r
+        receive_reset\r
+        send_data "250 Ok\r\n"\r
+      end\r
+\r
+      def unbind\r
+        connection_ended\r
+      end\r
+\r
+      #--\r
+      # STARTTLS may not be issued before EHLO, or unless the user has chosen\r
+      # to support it.\r
+      # TODO, must support user-supplied certificates.\r
+      #\r
+      def process_starttls\r
+        if @@parms[:starttls]\r
+          if @state.include?(:starttls)\r
+            send_data "503 TLS Already negotiated\r\n"\r
+          elsif ! @state.include?(:ehlo)\r
+            send_data "503 EHLO required before STARTTLS\r\n"\r
+          else\r
+            send_data "220 Start TLS negotiation\r\n"\r
+            start_tls\r
+            @state << :starttls\r
+          end\r
+        else\r
+          process_unknown\r
+        end\r
+      end\r
+\r
+\r
+      #--\r
+      # Requiring TLS is touchy, cf RFC2784.\r
+      # Requiring AUTH seems to be much more reasonable.\r
+      # We don't currently support any notion of deriving an authentication from the TLS\r
+      # negotiation, although that would certainly be reasonable.\r
+      # We DON'T allow MAIL FROM to be given twice.\r
+      # We DON'T enforce all the various rules for validating the sender or\r
+      # the reverse-path (like whether it should be null), and notifying the reverse\r
+      # path in case of delivery problems. All of that is left to the calling application.\r
+      #\r
+      def process_mail_from sender\r
+        if (@@parms[:starttls]==:required and !@state.include?(:starttls))\r
+          send_data "550 This server requires STARTTLS before MAIL FROM\r\n"\r
+        elsif (@@parms[:auth]==:required and !@state.include?(:auth))\r
+          send_data "550 This server requires authentication before MAIL FROM\r\n"\r
+        elsif @state.include?(:mail_from)\r
+          send_data "503 MAIL already given\r\n"\r
+        else\r
+          unless receive_sender sender\r
+            send_data "550 sender is unacceptable\r\n"\r
+          else\r
+            send_data "250 Ok\r\n"\r
+            @state << :mail_from\r
+          end\r
+        end\r
+      end\r
+\r
+      #--\r
+      # Since we require :mail_from to have been seen before we process RCPT TO,\r
+      # we don't need to repeat the tests for TLS and AUTH.\r
+      # Note that we don't remember or do anything else with the recipients.\r
+      # All of that is on the user code.\r
+      # TODO: we should enforce user-definable limits on the total number of\r
+      # recipients per transaction.\r
+      # We might want to make sure that a given recipient is only seen once, but\r
+      # for now we'll let that be the user's problem.\r
+      #\r
+      # User-written code can return a deferrable from receive_recipient.\r
+      #\r
+      def process_rcpt_to rcpt\r
+        unless @state.include?(:mail_from)\r
+          send_data "503 MAIL is required before RCPT\r\n"\r
+        else\r
+          succeeded = proc {\r
+            send_data "250 Ok\r\n"\r
+            @state << :rcpt unless @state.include?(:rcpt)\r
+          }\r
+          failed = proc {\r
+            send_data "550 recipient is unacceptable\r\n"\r
+          }\r
+\r
+          d = receive_recipient rcpt\r
+\r
+          if d.respond_to?(:set_deferred_status)\r
+            d.callback(&succeeded)\r
+            d.errback(&failed)\r
+          else\r
+            (d ? succeeded : failed).call\r
+          end\r
+\r
+=begin\r
+        unless receive_recipient rcpt\r
+          send_data "550 recipient is unacceptable\r\n"\r
+        else\r
+          send_data "250 Ok\r\n"\r
+          @state << :rcpt unless @state.include?(:rcpt)\r
+        end\r
+=end\r
+        end\r
+      end\r
+\r
+\r
+      # Send the incoming data to the application one chunk at a time, rather than\r
+      # one line at a time. That lets the application be a little more flexible about\r
+      # storing to disk, etc.\r
+      # Since we clear the chunk array every time we submit it, the caller needs to be\r
+      # aware to do things like dup it if he wants to keep it around across calls.\r
+      #\r
+      # DON'T reset the transaction upon disposition of the incoming message.\r
+      # This means another DATA command can be accepted with the same sender and recipients.\r
+      # If the client wants to reset, he can call RSET.\r
+      # Not sure whether the standard requires a transaction-reset at this point, but it\r
+      # appears not to.\r
+      #\r
+      # User-written code can return a Deferrable as a response from receive_message.\r
+      #\r
+      def process_data_line ln\r
+        if ln == "."\r
+          if @databuffer.length > 0\r
+            receive_data_chunk @databuffer\r
+            @databuffer.clear\r
+          end\r
+\r
+\r
+          succeeded = proc {\r
+            send_data "250 Message accepted\r\n"\r
+          }\r
+          failed = proc {\r
+            send_data "550 Message rejected\r\n"\r
+          }\r
+\r
+          d = receive_message\r
+\r
+          if d.respond_to?(:set_deferred_status)\r
+            d.callback(&succeeded)\r
+            d.errback(&failed)\r
+          else\r
+            (d ? succeeded : failed).call\r
+          end\r
+\r
+          @state.delete :data\r
+        else\r
+          # slice off leading . if any\r
+          ln.slice!(0...1) if ln[0] == 46\r
+          @databuffer << ln\r
+          if @databuffer.length > @@parms[:chunksize]\r
+            receive_data_chunk @databuffer\r
+            @databuffer.clear\r
+          end\r
+        end\r
+      end\r
+\r
+\r
+      #------------------------------------------\r
+      # Everything from here on can be overridden in user code.\r
+\r
+      # The greeting returned in the initial connection message to the client.\r
+      def get_server_greeting\r
+        "EventMachine SMTP Server"\r
+      end\r
+      # The domain name returned in the first line of the response to a\r
+      # successful EHLO or HELO command.\r
+      def get_server_domain\r
+        "Ok EventMachine SMTP Server"\r
+      end\r
+\r
+      # A false response from this user-overridable method will cause a\r
+      # 550 error to be returned to the remote client.\r
+      #\r
+      def receive_ehlo_domain domain\r
+        true\r
+      end\r
+\r
+      # Return true or false to indicate that the authentication is acceptable.\r
+      def receive_plain_auth user, password\r
+        true\r
+      end\r
+\r
+      # Receives the argument of the MAIL FROM command. Return false to\r
+      # indicate to the remote client that the sender is not accepted.\r
+      # This can only be successfully called once per transaction.\r
+      #\r
+      def receive_sender sender\r
+        true\r
+      end\r
+\r
+      # Receives the argument of a RCPT TO command. Can be given multiple\r
+      # times per transaction. Return false to reject the recipient.\r
+      #\r
+      def receive_recipient rcpt\r
+        true\r
+      end\r
+\r
+      # Sent when the remote peer issues the RSET command.\r
+      # Since RSET is not allowed to fail (according to the protocol),\r
+      # we ignore any return value from user overrides of this method.\r
+      #\r
+      def receive_reset\r
+      end\r
+\r
+      # Sent when the remote peer has ended the connection.\r
+      #\r
+      def connection_ended\r
+      end\r
+\r
+      # Called when the remote peer sends the DATA command.\r
+      # Returning false will cause us to send a 550 error to the peer.\r
+      # This can be useful for dealing with problems that arise from processing\r
+      # the whole set of sender and recipients.\r
+      #\r
+      def receive_data_command\r
+        true\r
+      end\r
+\r
+      # Sent when data from the remote peer is available. The size can be controlled\r
+      # by setting the :chunksize parameter. This call can be made multiple times.\r
+      # The goal is to strike a balance between sending the data to the application one\r
+      # line at a time, and holding all of a very large message in memory.\r
+      #\r
+      def receive_data_chunk data\r
+        @smtps_msg_size ||= 0\r
+        @smtps_msg_size += data.join.length\r
+        STDERR.write "<#{@smtps_msg_size}>"\r
+      end\r
+\r
+      # Sent after a message has been completely received. User code\r
+      # must return true or false to indicate whether the message has\r
+      # been accepted for delivery.\r
+      def receive_message\r
+        @@parms[:verbose] and $>.puts "Received complete message"\r
+        true\r
+      end\r
+\r
+      # This is called when the protocol state is reset. It happens\r
+      # when the remote client calls EHLO/HELO or RSET.\r
+      def receive_transaction\r
+      end\r
+    end\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/socks4.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/socks4.rb
new file mode 100644 (file)
index 0000000..132f320
--- /dev/null
@@ -0,0 +1,66 @@
+module EventMachine
+  module Protocols
+    # Basic SOCKS v4 client implementation
+    #
+    # Use as you would any regular connection:
+    #
+    # class MyConn < EM::P::Socks4
+    #   def post_init
+    #     send_data("sup")
+    #   end
+    #
+    #   def receive_data(data)
+    #     send_data("you said: #{data}")
+    #   end
+    # end
+    #
+    # EM.connect socks_host, socks_port, MyConn, host, port
+    #
+    class Socks4 < Connection
+      def initialize(host, port)
+        @host = Socket.gethostbyname(host).last
+        @port = port
+        @socks_error_code = nil
+        @buffer = ''
+        setup_methods
+      end
+
+      def setup_methods
+        class << self
+          def post_init; socks_post_init; end
+          def receive_data(*a); socks_receive_data(*a); end
+        end
+      end
+
+      def restore_methods
+        class << self
+          remove_method :post_init
+          remove_method :receive_data
+        end
+      end
+
+      def socks_post_init
+        header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C")
+        send_data(header)
+      end
+
+      def socks_receive_data(data)
+        @buffer << data
+        return  if @buffer.size < 8
+
+        header_resp = @buffer.slice! 0, 8
+        _, r = header_resp.unpack("cc")
+        if r != 90
+          @socks_error_code = r
+          close_connection
+          return
+        end
+
+        restore_methods
+
+        post_init
+        receive_data(@buffer)  unless @buffer.empty?
+      end
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/stomp.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/stomp.rb
new file mode 100644 (file)
index 0000000..e9e0e9f
--- /dev/null
@@ -0,0 +1,200 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 15 November 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+# \r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).\r
+    #\r
+    # == Usage example\r
+    #\r
+    #  module StompClient\r
+    #    include EM::Protocols::Stomp\r
+    #\r
+    #    def connection_completed\r
+    #      connect :login => 'guest', :passcode => 'guest'\r
+    #    end\r
+    #\r
+    #    def receive_msg msg\r
+    #      if msg.command == "CONNECTED"\r
+    #        subscribe '/some/topic'\r
+    #      else\r
+    #        p ['got a message', msg]\r
+    #        puts msg.body\r
+    #      end\r
+    #    end\r
+    #  end\r
+    #\r
+    #  EM.run{\r
+    #    EM.connect 'localhost', 61613, StompClient\r
+    #  }\r
+    #\r
+    module Stomp\r
+      include LineText2\r
+\r
+      class Message\r
+        # The command associated with the message, usually 'CONNECTED' or 'MESSAGE'\r
+        attr_accessor :command\r
+        # Hash containing headers such as destination and message-id\r
+        attr_accessor :header\r
+        alias :headers :header\r
+        # Body of the message\r
+        attr_accessor :body\r
+\r
+        def initialize # :nodoc:\r
+          @header = {}\r
+          @state = :precommand\r
+          @content_length = nil\r
+        end\r
+        def consume_line line # :nodoc:\r
+          if @state == :precommand\r
+            unless line =~ /\A\s*\Z/\r
+              @command = line\r
+              @state = :headers\r
+            end\r
+          elsif @state == :headers\r
+            if line == ""\r
+              if @content_length\r
+                yield( [:sized_text, @content_length+1] )\r
+              else\r
+                @state = :body\r
+                yield( [:unsized_text] )\r
+              end\r
+            elsif line =~ /\A([^:]+):(.+)\Z/\r
+              k = $1.dup.strip\r
+              v = $2.dup.strip\r
+              @header[k] = v\r
+              if k == "content-length"\r
+                @content_length = v.to_i\r
+              end\r
+            else\r
+              # This is a protocol error. How to signal it?\r
+            end\r
+          elsif @state == :body\r
+            @body = line\r
+            yield( [:dispatch] )\r
+          end\r
+        end\r
+      end\r
+\r
+      # :stopdoc:\r
+\r
+      def send_frame verb, headers={}, body=""\r
+        ary = [verb, "\n"]\r
+        headers.each {|k,v| ary << "#{k}:#{v}\n" }\r
+        ary << "content-length: #{body.to_s.length}\n"\r
+        ary << "content-type: text/plain; charset=UTF-8\n"\r
+        ary << "\n"\r
+        ary << body.to_s\r
+        ary << "\0"\r
+        send_data ary.join\r
+      end\r
+\r
+      def receive_line line\r
+        @stomp_initialized || init_message_reader\r
+        @stomp_message.consume_line(line) {|outcome|\r
+          if outcome.first == :sized_text\r
+            set_text_mode outcome[1]\r
+          elsif outcome.first == :unsized_text\r
+            set_delimiter "\0"\r
+          elsif outcome.first == :dispatch\r
+            receive_msg(@stomp_message) if respond_to?(:receive_msg)\r
+            init_message_reader\r
+          end\r
+        }\r
+      end\r
+\r
+      def receive_binary_data data\r
+        @stomp_message.body = data[0..-2]\r
+        receive_msg(@stomp_message) if respond_to?(:receive_msg)\r
+        init_message_reader\r
+      end\r
+\r
+      def init_message_reader\r
+        @stomp_initialized = true\r
+        set_delimiter "\n"\r
+        set_line_mode\r
+        @stomp_message = Message.new\r
+      end\r
+\r
+      # :startdoc:\r
+\r
+      # Invoked with an incoming Stomp::Message received from the STOMP server\r
+      def receive_msg msg\r
+        # stub, overwrite this in your handler\r
+      end\r
+\r
+      # CONNECT command, for authentication\r
+      #\r
+      #  connect :login => 'guest', :passcode => 'guest'\r
+      #\r
+      def connect parms={}\r
+        send_frame "CONNECT", parms\r
+      end\r
+\r
+      # SEND command, for publishing messages to a topic\r
+      #\r
+      #  send '/topic/name', 'some message here'\r
+      #\r
+      def send destination, body, parms={}\r
+        send_frame "SEND", parms.merge( :destination=>destination ), body.to_s\r
+      end\r
+\r
+      # SUBSCRIBE command, for subscribing to topics\r
+      #\r
+      #  subscribe '/topic/name', false\r
+      #\r
+      def subscribe dest, ack=false\r
+        send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}\r
+      end\r
+\r
+      # ACK command, for acknowledging receipt of messages\r
+      #\r
+      #  module StompClient\r
+      #    include EM::P::Stomp\r
+      #\r
+      #    def connection_completed\r
+      #      connect :login => 'guest', :passcode => 'guest'\r
+      #      # subscribe with ack mode\r
+      #      subscribe '/some/topic', true\r
+      #    end\r
+      #\r
+      #    def receive_msg msg\r
+      #      if msg.command == "MESSAGE"\r
+      #        ack msg.headers['message-id']\r
+      #        puts msg.body\r
+      #      end\r
+      #    end\r
+      #  end\r
+      #\r
+      def ack msgid\r
+        send_frame "ACK", 'message-id'=> msgid\r
+      end\r
+\r
+    end\r
+  end\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/tcptest.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/protocols/tcptest.rb
new file mode 100644 (file)
index 0000000..df776dd
--- /dev/null
@@ -0,0 +1,53 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 July 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+module EventMachine\r
+  module Protocols\r
+\r
+    class TcpConnectTester < Connection # :nodoc:\r
+      include EventMachine::Deferrable\r
+\r
+      def self.test( host, port )\r
+        EventMachine.connect( host, port, self )\r
+      end\r
+\r
+      def post_init\r
+        @start_time = Time.now\r
+      end\r
+\r
+      def connection_completed\r
+        @completed = true\r
+        set_deferred_status :succeeded, (Time.now - @start_time)\r
+        close_connection\r
+      end\r
+\r
+      def unbind\r
+        set_deferred_status :failed, (Time.now - @start_time)  unless @completed\r
+      end\r
+    end\r
+\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/queue.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/queue.rb
new file mode 100644 (file)
index 0000000..f18fbb7
--- /dev/null
@@ -0,0 +1,61 @@
+module EventMachine
+  # A cross thread, reactor scheduled, linear queue.
+  #
+  # This class provides a simple "Queue" like abstraction on top of the reactor
+  # scheduler. It services two primary purposes:
+  # * API sugar for stateful protocols
+  # * Pushing processing onto the same thread as the reactor
+  #
+  # See examples/ex_queue.rb for a detailed example.
+  #
+  #  q = EM::Queue.new
+  #  q.push('one', 'two', 'three')
+  #  3.times do
+  #    q.pop{ |msg| puts(msg) }
+  #  end
+  #
+  class Queue
+    # Create a new queue
+    def initialize
+      @items = []
+      @popq  = []
+    end
+
+    # Pop items off the queue, running the block on the reactor thread. The pop
+    # will not happen immediately, but at some point in the future, either in 
+    # the next tick, if the queue has data, or when the queue is populated.
+    def pop(*a, &b)
+      cb = EM::Callback(*a, &b)
+      EM.schedule do
+        if @items.empty?
+          @popq << cb
+        else
+          cb.call @items.shift
+        end
+      end
+      nil # Always returns nil
+    end
+
+    # Push items onto the queue in the reactor thread. The items will not appear
+    # in the queue immediately, but will be scheduled for addition during the 
+    # next reactor tick.
+    def push(*items)
+      EM.schedule do
+        @items.push(*items)
+        @popq.shift.call @items.shift until @items.empty? || @popq.empty?
+      end
+    end
+
+    # N.B. This is a peek, it's not thread safe, and may only tend toward 
+    # accuracy.
+    def empty?
+      @items.empty?
+    end
+
+    # N.B. This is a peek, it's not thread safe, and may only tend toward 
+    # accuracy.
+    def size
+      @items.size
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/spawnable.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/spawnable.rb
new file mode 100644 (file)
index 0000000..64a376d
--- /dev/null
@@ -0,0 +1,85 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 25 Aug 2007\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+\r
+module EventMachine\r
+  # Support for Erlang-style processes.\r
+  #\r
+  class SpawnedProcess\r
+    # Send a message to the spawned process\r
+    def notify *x\r
+      me = self\r
+      EM.next_tick {\r
+        # A notification executes in the context of this\r
+        # SpawnedProcess object. That makes self and notify\r
+        # work as one would expect.\r
+        #\r
+        y = me.call(*x)\r
+        if y and y.respond_to?(:pull_out_yield_block)\r
+          a,b = y.pull_out_yield_block\r
+          set_receiver a\r
+          self.notify if b\r
+        end\r
+      }\r
+    end\r
+    alias_method :resume, :notify\r
+    alias_method :run, :notify # for formulations like (EM.spawn {xxx}).run\r
+    #attr_accessor :receiver\r
+\r
+    #--\r
+    # I know I'm missing something stupid, but the inside of class << s\r
+    # can't see locally-bound values. It can see globals, though.\r
+    def set_receiver blk\r
+      $em______tmpglobal = blk\r
+      class << self\r
+        define_method :call, $em______tmpglobal.dup\r
+      end\r
+    end\r
+\r
+  end\r
+\r
+  class YieldBlockFromSpawnedProcess # :nodoc:\r
+    def initialize block, notify\r
+      @block = [block,notify]\r
+    end\r
+    def pull_out_yield_block\r
+      @block\r
+    end\r
+  end\r
+\r
+  # Spawn an erlang-style process\r
+  def self.spawn &block\r
+    s = SpawnedProcess.new\r
+    s.set_receiver block\r
+    s\r
+  end\r
+\r
+  def self.yield &block # :nodoc:\r
+    return YieldBlockFromSpawnedProcess.new( block, false )\r
+  end\r
+\r
+  def self.yield_and_notify &block # :nodoc:\r
+    return YieldBlockFromSpawnedProcess.new( block, true )\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/streamer.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/streamer.rb
new file mode 100644 (file)
index 0000000..d527d5e
--- /dev/null
@@ -0,0 +1,130 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 16 Jul 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+# \r
+\r
+\r
+module EventMachine\r
+  class FileStreamer\r
+    include Deferrable\r
+\r
+    # Use mapped streamer for files bigger than 16k\r
+    MappingThreshold = 16384\r
+    # Wait until next tick to send more data when 50k is still in the outgoing buffer\r
+    BackpressureLevel = 50000\r
+    # Send 16k chunks at a time\r
+    ChunkSize = 16384\r
+\r
+    # Stream a file over a given connection. An optional :http_chunks => true argument will\r
+    # use HTTP 1.1 style chunked-encoding semantics.\r
+    #\r
+    #  module FileSender\r
+    #    def post_init\r
+    #      streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar')\r
+    #      streamer.callback{\r
+    #        # file was sent successfully\r
+    #        close_connection_after_writing\r
+    #      }\r
+    #    end\r
+    #  end\r
+    #\r
+    def initialize connection, filename, args = {}\r
+      @connection = connection\r
+      @http_chunks = args[:http_chunks]\r
+\r
+      if File.exist?(filename)\r
+        @size = File.size?(filename)\r
+        if @size <= MappingThreshold\r
+          stream_without_mapping filename\r
+        else\r
+          stream_with_mapping filename\r
+        end\r
+      else\r
+        fail "file not found"\r
+      end\r
+    end\r
+\r
+    def stream_without_mapping filename # :nodoc:\r
+      if @http_chunks\r
+        @connection.send_data "#{@size.to_s(16)}\r\n"\r
+        @connection.send_file_data filename\r
+        @connection.send_data "\r\n0\r\n\r\n"\r
+      else\r
+        @connection.send_file_data filename\r
+      end\r
+      succeed\r
+    end\r
+    private :stream_without_mapping\r
+\r
+    def stream_with_mapping filename # :nodoc:\r
+      ensure_mapping_extension_is_present\r
+\r
+      @position = 0\r
+      @mapping = EventMachine::FastFileReader::Mapper.new filename\r
+      stream_one_chunk\r
+    end\r
+    private :stream_with_mapping\r
+\r
+    # Used internally to stream one chunk at a time over multiple reactor ticks\r
+    def stream_one_chunk\r
+      loop {\r
+        if @position < @size\r
+          if @connection.get_outbound_data_size > BackpressureLevel\r
+            EventMachine::next_tick {stream_one_chunk}\r
+            break\r
+          else\r
+            len = @size - @position\r
+            len = ChunkSize if (len > ChunkSize)\r
+\r
+            @connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks\r
+            @connection.send_data( @mapping.get_chunk( @position, len ))\r
+            @connection.send_data("\r\n") if @http_chunks\r
+\r
+            @position += len\r
+          end\r
+        else\r
+          @connection.send_data "0\r\n\r\n" if @http_chunks\r
+          @mapping.close\r
+          succeed\r
+          break\r
+        end\r
+      }\r
+    end\r
+\r
+    #--\r
+    # We use an outboard extension class to get memory-mapped files.\r
+    # It's outboard to avoid polluting the core distro, but that means\r
+    # there's a "hidden" dependency on it. The first time we get here in\r
+    # any run, try to load up the dependency extension. User code will see\r
+    # a LoadError if it's not available, but code that doesn't require\r
+    # mapped files will work fine without it. This is a somewhat difficult\r
+    # compromise between usability and proper modularization.\r
+    #\r
+    def ensure_mapping_extension_is_present # :nodoc:\r
+      @@fastfilereader ||= (require 'fastfilereaderext')\r
+    end\r
+    private :ensure_mapping_extension_is_present\r
+\r
+  end\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/timers.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/timers.rb
new file mode 100644 (file)
index 0000000..e03b869
--- /dev/null
@@ -0,0 +1,56 @@
+module EventMachine
+  # Creates a one-time timer
+  #
+  #  timer = EventMachine::Timer.new(5) do
+  #    # this will never fire because we cancel it
+  #  end
+  #  timer.cancel
+  #
+  class Timer
+    # Create a new timer that fires after a given number of seconds
+    def initialize interval, callback=nil, &block
+      @signature = EventMachine::add_timer(interval, callback || block)
+    end
+
+    # Cancel the timer
+    def cancel
+      EventMachine.send :cancel_timer, @signature
+    end
+  end
+
+  # Creates a periodic timer
+  #
+  #  n = 0
+  #  timer = EventMachine::PeriodicTimer.new(5) do
+  #    puts "the time is #{Time.now}"
+  #    timer.cancel if (n+=1) > 5
+  #  end
+  #
+  class PeriodicTimer
+    # Create a new periodic timer that executes every interval seconds
+    def initialize interval, callback=nil, &block
+      @interval = interval
+      @code = callback || block
+      @cancelled = false
+      schedule
+    end
+
+    # Cancel the periodic timer
+    def cancel
+      @cancelled = true
+    end
+
+    # Fire the timer every interval seconds
+    attr_accessor :interval
+
+    def schedule # :nodoc:
+      EventMachine::add_timer @interval, method(:fire)
+    end
+    def fire # :nodoc:
+      unless @cancelled
+        @code.call
+        schedule
+      end
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/version.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/em/version.rb
new file mode 100644 (file)
index 0000000..c39a6a4
--- /dev/null
@@ -0,0 +1,3 @@
+module EventMachine
+  VERSION = "0.12.10"
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/eventmachine.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/eventmachine.rb
new file mode 100644 (file)
index 0000000..4106096
--- /dev/null
@@ -0,0 +1,1592 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 Apr 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+
+#-- Select in a library based on a global variable.
+# PROVISIONALLY commented out this whole mechanism which selects
+# a pure-Ruby EM implementation if the extension is not available.
+# I expect this will cause a lot of people's code to break, as it
+# exposes misconfigurations and path problems that were masked up
+# till now. The reason I'm disabling it is because the pure-Ruby
+# code will have problems of its own, and it's not nearly as fast
+# anyway. Suggested by a problem report from Moshe Litvin. 05Jun07.
+#
+# 05Dec07: Re-enabled the pure-ruby mechanism, but without the automatic
+# fallback feature that tripped up Moshe Litvin. We shouldn't fail over to
+# the pure Ruby version because it's possible that the user intended to
+# run the extension but failed to do so because of a compilation or
+# similar error. So we require either a global variable or an environment
+# string be set in order to select the pure-Ruby version.
+#
+
+
+unless defined?($eventmachine_library)
+  $eventmachine_library = ENV['EVENTMACHINE_LIBRARY'] || :cascade
+end
+$eventmachine_library = $eventmachine_library.to_sym
+
+case $eventmachine_library
+when :pure_ruby
+  require 'pr_eventmachine'
+when :extension
+  require 'rubyeventmachine'
+when :java
+  require 'jeventmachine'
+else # :cascade
+  # This is the case that most user code will take.
+  # Prefer the extension if available.
+  begin
+    if RUBY_PLATFORM =~ /java/
+      require 'java'
+      require 'jeventmachine'
+      $eventmachine_library = :java
+    else
+      require 'rubyeventmachine'
+      $eventmachine_library = :extension
+    end
+  rescue LoadError
+    warn "# EventMachine fell back to pure ruby mode" if $DEBUG
+    require 'pr_eventmachine'
+    $eventmachine_library = :pure_ruby
+  end
+end
+
+require "em/version"
+require 'em/deferrable'
+require 'em/future'
+require 'em/streamer'
+require 'em/spawnable'
+require 'em/processes'
+require 'em/buftok'
+require 'em/timers'
+require 'em/protocols'
+require 'em/connection'
+require 'em/callback'
+require 'em/queue'
+require 'em/channel'
+require 'em/file_watch'
+require 'em/process_watch'
+
+require 'shellwords'
+require 'thread'
+
+# == Introduction
+# EventMachine provides a fast, lightweight framework for implementing
+# Ruby programs that can use the network to communicate with other
+# processes. Using EventMachine, Ruby programmers can easily connect
+# to remote servers and act as servers themselves. EventMachine does not
+# supplant the Ruby IP libraries. It does provide an alternate technique
+# for those applications requiring better performance, scalability,
+# and discipline over the behavior of network sockets, than is easily
+# obtainable using the built-in libraries, especially in applications
+# which are structurally well-suited for the event-driven programming model.
+#
+# EventMachine provides a perpetual event-loop which your programs can
+# start and stop. Within the event loop, TCP network connections are
+# initiated and accepted, based on EventMachine methods called by your
+# program. You also define callback methods which are called by EventMachine
+# when events of interest occur within the event-loop.
+#
+# User programs will be called back when the following events occur:
+# * When the event loop accepts network connections from remote peers
+# * When data is received from network connections
+# * When connections are closed, either by the local or the remote side
+# * When user-defined timers expire
+#
+# == Usage example
+#
+# Here's a fully-functional echo server implemented in EventMachine:
+#
+#  require 'eventmachine'
+#
+#  module EchoServer
+#    def post_init
+#      puts "-- someone connected to the echo server!"
+#    end
+#
+#    def receive_data data
+#      send_data ">>>you sent: #{data}"
+#      close_connection if data =~ /quit/i
+#    end
+#
+#    def unbind
+#      puts "-- someone disconnected from the echo server!"
+#    end
+#  end
+#
+#  EventMachine::run {
+#    EventMachine::start_server "127.0.0.1", 8081, EchoServer
+#  }
+#
+# What's going on here? Well, we have defined the module EchoServer to
+# implement the semantics of the echo protocol (more about that shortly).
+# The last three lines invoke the event-machine itself, which runs forever
+# unless one of your callbacks terminates it. The block that you supply
+# to EventMachine::run contains code that runs immediately after the event
+# machine is initialized and before it starts looping. This is the place
+# to open up a TCP server by specifying the address and port it will listen
+# on, together with the module that will process the data.
+# 
+# Our EchoServer is extremely simple as the echo protocol doesn't require
+# much work. Basically you want to send back to the remote peer whatever
+# data it sends you. We'll dress it up with a little extra text to make it
+# interesting. Also, we'll close the connection in case the received data
+# contains the word "quit."
+# 
+# So what about this module EchoServer? Well, whenever a network connection
+# (either a client or a server) starts up, EventMachine instantiates an anonymous
+# class, that your module has been mixed into. Exactly one of these class
+# instances is created for each connection. Whenever an event occurs on a
+# given connection, its corresponding object automatically calls specific
+# instance methods which your module may redefine. The code in your module
+# always runs in the context of a class instance, so you can create instance
+# variables as you wish and they will be carried over to other callbacks
+# made on that same connection.
+# 
+# Looking back up at EchoServer, you can see that we've defined the method
+# receive_data which (big surprise) is called whenever data has been received
+# from the remote end of the connection. Very simple. We get the data
+# (a String object) and can do whatever we wish with it. In this case,
+# we use the method send_data to return the received data to the caller,
+# with some extra text added in. And if the user sends the word "quit,"
+# we'll close the connection with (naturally) close_connection.
+# (Notice that closing the connection doesn't terminate the processing loop,
+# or change the fact that your echo server is still accepting connections!) 
+#
+# == Questions and Futures
+# Would it be useful for EventMachine to incorporate the Observer pattern
+# and make use of the corresponding Ruby <tt>observer</tt> package?
+# Interesting thought.
+#
+module EventMachine
+  class <<self
+    # Exposed to allow joining on the thread, when run in a multithreaded
+    # environment. Performing other actions on the thread has undefined
+    # semantics.
+    attr_reader :reactor_thread
+  end
+  @next_tick_mutex = Mutex.new
+  @reactor_running = false
+  @next_tick_queue = nil
+  @threadpool = nil
+  
+
+  # EventMachine::run initializes and runs an event loop.
+  # This method only returns if user-callback code calls stop_event_loop.
+  # Use the supplied block to define your clients and servers.
+  # The block is called by EventMachine::run immediately after initializing
+  # its internal event loop but <i>before</i> running the loop.
+  # Therefore this block is the right place to call start_server if you
+  # want to accept connections from remote clients.
+  #
+  # For programs that are structured as servers, it's usually appropriate
+  # to start an event loop by calling EventMachine::run, and let it
+  # run forever. It's also possible to use EventMachine::run to make a single
+  # client-connection to a remote server, process the data flow from that
+  # single connection, and then call stop_event_loop to force EventMachine::run
+  # to return. Your program will then continue from the point immediately
+  # following the call to EventMachine::run.
+  #
+  # You can of course do both client and servers simultaneously in the same program.
+  # One of the strengths of the event-driven programming model is that the
+  # handling of network events on many different connections will be interleaved,
+  # and scheduled according to the actual events themselves. This maximizes
+  # efficiency.
+  #
+  # === Server usage example
+  #
+  # See EventMachine.start_server
+  #
+  # === Client usage example
+  #
+  # See EventMachine.connect
+  #
+  #--
+  # Obsoleted the use_threads mechanism.
+  # 25Nov06: Added the begin/ensure block. We need to be sure that release_machine
+  # gets called even if an exception gets thrown within any of the user code
+  # that the event loop runs. The best way to see this is to run a unit
+  # test with two functions, each of which calls EventMachine#run and each of
+  # which throws something inside of #run. Without the ensure, the second test
+  # will start without release_machine being called and will immediately throw
+  # a C++ runtime error.
+  #
+  def self.run blk=nil, tail=nil, &block
+    @tails ||= []
+    tail and @tails.unshift(tail)
+
+    if reactor_running?
+      (b = blk || block) and b.call # next_tick(b)
+    else
+      @conns = {}
+      @acceptors = {}
+      @timers = {}
+      @wrapped_exception = nil
+      @next_tick_queue ||= []
+      begin
+        @reactor_running = true
+        initialize_event_machine
+        (b = blk || block) and add_timer(0, b)
+        if @next_tick_queue && !@next_tick_queue.empty?
+          add_timer(0) { signal_loopbreak }
+        end
+        @reactor_thread = Thread.current
+        run_machine
+      ensure
+        until @tails.empty?
+          @tails.pop.call
+        end
+
+        begin
+          release_machine
+        ensure
+          if @threadpool
+            @threadpool.each { |t| t.exit }
+            @threadpool.each do |t|
+              next unless t.alive?
+              # ruby 1.9 has no kill!
+              t.respond_to?(:kill!) ? t.kill! : t.kill
+            end
+            @threadqueue = nil
+            @resultqueue = nil
+            @threadpool = nil
+          end
+
+          @next_tick_queue = nil
+        end
+        @reactor_running = false
+        @reactor_thread = nil
+      end
+
+      raise @wrapped_exception if @wrapped_exception
+    end
+  end
+
+  # Sugars a common use case. Will pass the given block to #run, but will terminate
+  # the reactor loop and exit the function as soon as the code in the block completes.
+  # (Normally, #run keeps running indefinitely, even after the block supplied to it
+  # finishes running, until user code calls #stop.)
+  #
+  def self.run_block &block
+    pr = proc {
+      block.call
+      EventMachine::stop
+    }
+    run(&pr)
+  end
+
+  # Returns true if the calling thread is the same thread as the reactor.
+  def self.reactor_thread?
+    Thread.current == @reactor_thread
+  end
+
+  # Runs the given callback on the reactor thread, or immediately if called
+  # from the reactor thread. Accepts the same arguments as EM::Callback
+  def self.schedule(*a, &b)
+    cb = Callback(*a, &b)
+    if reactor_running? && reactor_thread?
+      cb.call
+    else
+      next_tick { cb.call }
+    end
+  end
+
+  # fork_reactor forks a new process and calls EM#run inside of it, passing your block.
+  #--
+  # This implementation is subject to change, especially if we clean up the relationship
+  # of EM#run to @reactor_running.
+  # Original patch by Aman Gupta.
+  #
+  def self.fork_reactor &block
+    Kernel.fork do
+      if self.reactor_running?
+        self.stop_event_loop
+        self.release_machine
+        self.instance_variable_set( '@reactor_running', false )
+      end
+      self.run block
+    end
+  end
+
+  # EventMachine#add_timer adds a one-shot timer to the event loop.
+  # Call it with one or two parameters. The first parameters is a delay-time
+  # expressed in <i>seconds</i> (not milliseconds). The second parameter, if
+  # present, must be a proc object. If a proc object is not given, then you
+  # can also simply pass a block to the method call.
+  #
+  # EventMachine#add_timer may be called from the block passed to EventMachine#run
+  # or from any callback method. It schedules execution of the proc or block
+  # passed to add_timer, after the passage of an interval of time equal to
+  # <i>at least</i> the number of seconds specified in the first parameter to
+  # the call.
+  #
+  # EventMachine#add_timer is a <i>non-blocking</i> call. Callbacks can and will
+  # be called during the interval of time that the timer is in effect.
+  # There is no built-in limit to the number of timers that can be outstanding at
+  # any given time.
+  #
+  # === Usage example
+  #
+  # This example shows how easy timers are to use. Observe that two timers are
+  # initiated simultaneously. Also, notice that the event loop will continue
+  # to run even after the second timer event is processed, since there was
+  # no call to EventMachine#stop_event_loop. There will be no activity, of
+  # course, since no network clients or servers are defined. Stop the program
+  # with Ctrl-C.
+  #
+  #  EventMachine::run {
+  #    puts "Starting the run now: #{Time.now}"
+  #    EventMachine::add_timer 5, proc { puts "Executing timer event: #{Time.now}" }
+  #    EventMachine::add_timer( 10 ) { puts "Executing timer event: #{Time.now}" }
+  #  }
+  #
+  #
+  # Also see EventMachine::Timer
+  #--
+  # Changed 04Oct06: We now pass the interval as an integer number of milliseconds.
+  #
+  def self.add_timer *args, &block
+    interval = args.shift
+    code = args.shift || block
+    if code
+      # check too many timers!
+      s = add_oneshot_timer((interval.to_f * 1000).to_i)
+      @timers[s] = code
+      s
+    end
+  end
+
+  # EventMachine#add_periodic_timer adds a periodic timer to the event loop.
+  # It takes the same parameters as the one-shot timer method, EventMachine#add_timer.
+  # This method schedules execution of the given block repeatedly, at intervals
+  # of time <i>at least</i> as great as the number of seconds given in the first
+  # parameter to the call.
+  # 
+  # === Usage example
+  #
+  # The following sample program will write a dollar-sign to stderr every five seconds.
+  # (Of course if the program defined network clients and/or servers, they would
+  # be doing their work while the periodic timer is counting off.)
+  #
+  #  EventMachine::run {
+  #    EventMachine::add_periodic_timer( 5 ) { $stderr.write "$" }
+  #  }
+  #
+  #
+  # Also see EventMachine::PeriodicTimer
+  #
+  def self.add_periodic_timer *args, &block
+    interval = args.shift
+    code = args.shift || block
+
+    EventMachine::PeriodicTimer.new(interval, code)
+  end
+
+  # Cancel a timer using its signature. You can also use EventMachine::Timer#cancel
+  #
+  def self.cancel_timer timer_or_sig
+    if timer_or_sig.respond_to? :cancel
+      timer_or_sig.cancel
+    else
+      @timers[timer_or_sig] = false if @timers.has_key?(timer_or_sig)
+    end
+  end
+
+
+  # stop_event_loop may called from within a callback method
+  # while EventMachine's processing loop is running.
+  # It causes the processing loop to stop executing, which
+  # will cause all open connections and accepting servers
+  # to be run down and closed. <i>Callbacks for connection-termination
+  # will be called</i> as part of the processing of stop_event_loop.
+  # (There currently is no option to panic-stop the loop without
+  # closing connections.) When all of this processing is complete,
+  # the call to EventMachine::run which started the processing loop
+  # will return and program flow will resume from the statement
+  # following EventMachine::run call.
+  #
+  # === Usage example
+  #
+  #  require 'rubygems'
+  #  require 'eventmachine'
+  #
+  #  module Redmond
+  #    def post_init
+  #      puts "We're sending a dumb HTTP request to the remote peer."
+  #      send_data "GET / HTTP/1.1\r\nHost: www.microsoft.com\r\n\r\n"
+  #    end
+  #  
+  #    def receive_data data
+  #      puts "We received #{data.length} bytes from the remote peer."
+  #      puts "We're going to stop the event loop now."
+  #      EventMachine::stop_event_loop
+  #    end
+  #  
+  #    def unbind
+  #      puts "A connection has terminated."
+  #    end
+  #  end
+  #  
+  #  puts "We're starting the event loop now."
+  #  EventMachine::run {
+  #    EventMachine::connect "www.microsoft.com", 80, Redmond
+  #  }
+  #  puts "The event loop has stopped."
+  #  
+  # This program will produce approximately the following output:
+  #
+  #  We're starting the event loop now.
+  #  We're sending a dumb HTTP request to the remote peer.
+  #  We received 1440 bytes from the remote peer.
+  #  We're going to stop the event loop now.
+  #  A connection has terminated.
+  #  The event loop has stopped.
+  #
+  #
+  def self.stop_event_loop
+    EventMachine::stop
+  end
+
+  # EventMachine::start_server initiates a TCP server (socket
+  # acceptor) on the specified IP address and port.
+  # The IP address must be valid on the machine where the program
+  # runs, and the process must be privileged enough to listen
+  # on the specified port (on Unix-like systems, superuser privileges
+  # are usually required to listen on any port lower than 1024).
+  # Only one listener may be running on any given address/port
+  # combination. start_server will fail if the given address and port
+  # are already listening on the machine, either because of a prior call
+  # to start_server or some unrelated process running on the machine.
+  # If start_server succeeds, the new network listener becomes active
+  # immediately and starts accepting connections from remote peers,
+  # and these connections generate callback events that are processed
+  # by the code specified in the handler parameter to start_server.
+  #
+  # The optional handler which is passed to start_server is the key
+  # to EventMachine's ability to handle particular network protocols.
+  # The handler parameter passed to start_server must be a Ruby Module
+  # that you must define. When the network server that is started by
+  # start_server accepts a new connection, it instantiates a new
+  # object of an anonymous class that is inherited from EventMachine::Connection,
+  # <i>into which the methods from your handler have been mixed.</i>
+  # Your handler module may redefine any of the methods in EventMachine::Connection
+  # in order to implement the specific behavior of the network protocol.
+  #
+  # Callbacks invoked in response to network events <i>always</i> take place
+  # within the execution context of the object derived from EventMachine::Connection
+  # extended by your handler module. There is one object per connection, and
+  # all of the callbacks invoked for a particular connection take the form
+  # of instance methods called against the corresponding EventMachine::Connection
+  # object. Therefore, you are free to define whatever instance variables you
+  # wish, in order to contain the per-connection state required by the network protocol you are
+  # implementing.
+  #
+  # start_server is often called inside the block passed to EventMachine::run,
+  # but it can be called from any EventMachine callback. start_server will fail
+  # unless the EventMachine event loop is currently running (which is why
+  # it's often called in the block suppled to EventMachine::run).
+  #
+  # You may call start_server any number of times to start up network
+  # listeners on different address/port combinations. The servers will
+  # all run simultaneously. More interestingly, each individual call to start_server
+  # can specify a different handler module and thus implement a different
+  # network protocol from all the others.
+  #
+  # === Usage example
+  # Here is an example of a server that counts lines of input from the remote
+  # peer and sends back the total number of lines received, after each line.
+  # Try the example with more than one client connection opened via telnet,
+  # and you will see that the line count increments independently on each
+  # of the client connections. Also very important to note, is that the
+  # handler for the receive_data function, which our handler redefines, may
+  # not assume that the data it receives observes any kind of message boundaries.
+  # Also, to use this example, be sure to change the server and port parameters
+  # to the start_server call to values appropriate for your environment.
+  #
+  #  require 'rubygems'
+  #  require 'eventmachine'
+  #
+  #  module LineCounter
+  #    MaxLinesPerConnection = 10
+  #  
+  #    def post_init
+  #      puts "Received a new connection"
+  #      @data_received = ""
+  #      @line_count = 0
+  #    end
+  #  
+  #    def receive_data data
+  #      @data_received << data
+  #      while @data_received.slice!( /^[^\n]*[\n]/m )
+  #        @line_count += 1
+  #        send_data "received #{@line_count} lines so far\r\n"
+  #        @line_count == MaxLinesPerConnection and close_connection_after_writing
+  #      end
+  #    end
+  #  end
+  #  
+  #  EventMachine::run {
+  #    host,port = "192.168.0.100", 8090
+  #    EventMachine::start_server host, port, LineCounter
+  #    puts "Now accepting connections on address #{host}, port #{port}..."
+  #    EventMachine::add_periodic_timer( 10 ) { $stderr.write "*" }
+  #  }
+  #  
+  #
+  def self.start_server server, port=nil, handler=nil, *args, &block
+    begin
+      port = Integer(port)
+    rescue ArgumentError, TypeError
+      # there was no port, so server must be a unix domain socket
+      # the port argument is actually the handler, and the handler is one of the args
+      args.unshift handler if handler
+      handler = port
+      port = nil
+    end if port
+
+    klass = klass_from_handler(Connection, handler, *args)
+
+    s = if port
+          start_tcp_server server, port
+        else
+          start_unix_server server
+        end
+    @acceptors[s] = [klass,args,block]
+    s
+  end
+
+
+  # Stop a TCP server socket that was started with EventMachine#start_server.
+  #--
+  # Requested by Kirk Haines. TODO, this isn't OOP enough. We ought somehow
+  # to have #start_server return an object that has a close or a stop method on it.
+  #
+  def self.stop_server signature
+    EventMachine::stop_tcp_server signature
+  end
+
+  # Start a Unix-domain server
+  #
+  # Note that this is an alias for EventMachine::start_server, which can be used to start both
+  # TCP and Unix-domain servers
+  def self.start_unix_domain_server filename, *args, &block
+    start_server filename, *args, &block
+  end
+
+  # EventMachine#connect initiates a TCP connection to a remote
+  # server and sets up event-handling for the connection.
+  # You can call EventMachine#connect in the block supplied
+  # to EventMachine#run or in any callback method.
+  #
+  # EventMachine#connect takes the IP address (or hostname) and
+  # port of the remote server you want to connect to.
+  # It also takes an optional handler Module which you must define, that
+  # contains the callbacks that will be invoked by the event loop
+  # on behalf of the connection.
+  #
+  # See the description of EventMachine#start_server for a discussion
+  # of the handler Module. All of the details given in that description
+  # apply for connections created with EventMachine#connect.
+  #
+  # === Usage Example
+  #
+  # Here's a program which connects to a web server, sends a naive
+  # request, parses the HTTP header of the response, and then
+  # (antisocially) ends the event loop, which automatically drops the connection
+  # (and incidentally calls the connection's unbind method).
+  # 
+  #  module DumbHttpClient
+  #    def post_init
+  #      send_data "GET / HTTP/1.1\r\nHost: _\r\n\r\n"
+  #      @data = ""
+  #      @parsed = false
+  #    end
+  #  
+  #    def receive_data data
+  #      @data << data
+  #      if !@parsed and @data =~ /[\n][\r]*[\n]/m
+  #        @parsed = true
+  #        puts "RECEIVED HTTP HEADER:"
+  #        $`.each {|line| puts ">>> #{line}" }
+  #  
+  #        puts "Now we'll terminate the loop, which will also close the connection"
+  #        EventMachine::stop_event_loop
+  #      end
+  #    end
+  #  
+  #    def unbind
+  #      puts "A connection has terminated"
+  #    end
+  #  end
+  #  
+  #  EventMachine::run {
+  #    EventMachine::connect "www.bayshorenetworks.com", 80, DumbHttpClient
+  #  }
+  #  puts "The event loop has ended"
+  #  
+  #
+  # There are times when it's more convenient to define a protocol handler
+  # as a Class rather than a Module. Here's how to do this:
+  #
+  #  class MyProtocolHandler < EventMachine::Connection
+  #    def initialize *args
+  #      super
+  #      # whatever else you want to do here
+  #    end
+  #    
+  #    #.......your other class code
+  #  end
+  #
+  # If you do this, then an instance of your class will be instantiated to handle
+  # every network connection created by your code or accepted by servers that you
+  # create. If you redefine #post_init in your protocol-handler class, your
+  # #post_init method will be called _inside_ the call to #super that you will
+  # make in your #initialize method (if you provide one).
+  #
+  #--
+  # EventMachine::connect initiates a TCP connection to a remote
+  # server and sets up event-handling for the connection.
+  # It internally creates an object that should not be handled
+  # by the caller. HOWEVER, it's often convenient to get the
+  # object to set up interfacing to other objects in the system.
+  # We return the newly-created anonymous-class object to the caller.
+  # It's expected that a considerable amount of code will depend
+  # on this behavior, so don't change it.
+  #
+  # Ok, added support for a user-defined block, 13Apr06.
+  # This leads us to an interesting choice because of the
+  # presence of the post_init call, which happens in the
+  # initialize method of the new object. We call the user's
+  # block and pass the new object to it. This is a great
+  # way to do protocol-specific initiation. It happens
+  # AFTER post_init has been called on the object, which I
+  # certainly hope is the right choice.
+  # Don't change this lightly, because accepted connections
+  # are different from connected ones and we don't want
+  # to have them behave differently with respect to post_init
+  # if at all possible.
+  #
+  def self.connect server, port=nil, handler=nil, *args, &blk
+    bind_connect nil, nil, server, port, handler, *args, &blk
+  end
+
+  # EventMachine::bind_connect is like EventMachine::connect, but allows for a local address/port
+  # to bind the connection to.
+  def self.bind_connect bind_addr, bind_port, server, port=nil, handler=nil, *args
+    begin
+      port = Integer(port)
+    rescue ArgumentError, TypeError
+      # there was no port, so server must be a unix domain socket
+      # the port argument is actually the handler, and the handler is one of the args
+      args.unshift handler if handler
+      handler = port
+      port = nil
+    end if port
+
+    klass = klass_from_handler(Connection, handler, *args)
+
+    s = if port
+          if bind_addr
+            bind_connect_server bind_addr, bind_port.to_i, server, port
+          else
+            connect_server server, port
+          end
+        else
+          connect_unix_server server
+        end
+
+    c = klass.new s, *args
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+  # EventMachine::watch registers a given file descriptor or IO object with the eventloop. The
+  # file descriptor will not be modified (it will remain blocking or non-blocking).
+  #
+  # The eventloop can be used to process readable and writable events on the file descriptor, using
+  # EventMachine::Connection#notify_readable= and EventMachine::Connection#notify_writable=
+  #
+  # EventMachine::Connection#notify_readable? and EventMachine::Connection#notify_writable? can be used
+  # to check what events are enabled on the connection.
+  #
+  # To detach the file descriptor, use EventMachine::Connection#detach
+  #
+  # === Usage Example
+  #
+  #  module SimpleHttpClient
+  #    def notify_readable
+  #      header = @io.readline
+  #
+  #      if header == "\r\n"
+  #        # detach returns the file descriptor number (fd == @io.fileno)
+  #        fd = detach
+  #      end
+  #    rescue EOFError
+  #      detach
+  #    end
+  #
+  #    def unbind
+  #      EM.next_tick do
+  #        # socket is detached from the eventloop, but still open
+  #        data = @io.read
+  #      end
+  #    end
+  #  end
+  #
+  #  EM.run{
+  #    $sock = TCPSocket.new('site.com', 80)
+  #    $sock.write("GET / HTTP/1.0\r\n\r\n")
+  #    conn = EM.watch $sock, SimpleHttpClient
+  #    conn.notify_readable = true
+  #  }
+  #
+  #--
+  # Thanks to Riham Aldakkak (eSpace Technologies) for the initial patch
+  def EventMachine::watch io, handler=nil, *args, &blk
+    attach_io io, true, handler, *args, &blk
+  end
+
+  # Attaches an IO object or file descriptor to the eventloop as a regular connection.
+  # The file descriptor will be set as non-blocking, and EventMachine will process
+  # receive_data and send_data events on it as it would for any other connection.
+  #
+  # To watch a fd instead, use EventMachine::watch, which will not alter the state of the socket
+  # and fire notify_readable and notify_writable events instead.
+  def EventMachine::attach io, handler=nil, *args, &blk
+    attach_io io, false, handler, *args, &blk
+  end
+
+  def EventMachine::attach_io io, watch_mode, handler=nil, *args # :nodoc:
+    klass = klass_from_handler(Connection, handler, *args)
+
+    if !watch_mode and klass.public_instance_methods.any?{|m| [:notify_readable, :notify_writable].include? m.to_sym }
+      raise ArgumentError, "notify_readable/writable with EM.attach is not supported. Use EM.watch(io){ |c| c.notify_readable = true }"
+    end
+
+    if io.respond_to?(:fileno)
+      fd = defined?(JRuby) ? JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel : io.fileno
+    else
+      fd = io
+    end
+
+    s = attach_fd fd, watch_mode
+    c = klass.new s, *args
+
+    c.instance_variable_set(:@io, io)
+    c.instance_variable_set(:@fd, fd)
+
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+
+  # Connect to a given host/port and re-use the provided EventMachine::Connection instance
+  #--
+  # Observe, the test for already-connected FAILS if we call a reconnect inside post_init,
+  # because we haven't set up the connection in @conns by that point.
+  # RESIST THE TEMPTATION to "fix" this problem by redefining the behavior of post_init.
+  #
+  # Changed 22Nov06: if called on an already-connected handler, just return the
+  # handler and do nothing more. Originally this condition raised an exception.
+  # We may want to change it yet again and call the block, if any.
+  #
+  def self.reconnect server, port, handler # :nodoc:
+    raise "invalid handler" unless handler.respond_to?(:connection_completed)
+    #raise "still connected" if @conns.has_key?(handler.signature)
+    return handler if @conns.has_key?(handler.signature)
+
+    s = connect_server server, port
+    handler.signature = s
+    @conns[s] = handler
+    block_given? and yield handler
+    handler
+  end
+
+
+  # Make a connection to a Unix-domain socket. This is not implemented on Windows platforms.
+  # The parameter socketname is a String which identifies the Unix-domain socket you want
+  # to connect to. socketname is the name of a file on your local system, and in most cases
+  # is a fully-qualified path name. Make sure that your process has enough local permissions
+  # to open the Unix-domain socket.
+  # See also the documentation for #connect. This method behaves like #connect
+  # in all respects except for the fact that it connects to a local Unix-domain
+  # socket rather than a TCP socket.
+  #
+  # Note that this method is simply an alias for #connect, which can connect to both TCP
+  # and Unix-domain sockets
+  #--
+  # For making connections to Unix-domain sockets.
+  # Eventually this has to get properly documented and unified with the TCP-connect methods.
+  # Note how nearly identical this is to EventMachine#connect
+  def self.connect_unix_domain socketname, *args, &blk
+    connect socketname, *args, &blk
+  end
+
+
+  # EventMachine#open_datagram_socket is for support of UDP-based
+  # protocols. Its usage is similar to that of EventMachine#start_server.
+  # It takes three parameters: an IP address (which must be valid
+  # on the machine which executes the method), a port number,
+  # and an optional Module name which will handle the data.
+  # This method will create a new UDP (datagram) socket and
+  # bind it to the address and port that you specify.
+  # The normal callbacks (see EventMachine#start_server) will
+  # be called as events of interest occur on the newly-created
+  # socket, but there are some differences in how they behave.
+  #
+  # Connection#receive_data will be called when a datagram packet
+  # is received on the socket, but unlike TCP sockets, the message
+  # boundaries of the received data will be respected. In other words,
+  # if the remote peer sent you a datagram of a particular size,
+  # you may rely on Connection#receive_data to give you the
+  # exact data in the packet, with the original data length.
+  # Also observe that Connection#receive_data may be called with a
+  # <i>zero-length</i> data payload, since empty datagrams are permitted
+  # in UDP.
+  #
+  # Connection#send_data is available with UDP packets as with TCP,
+  # but there is an important difference. Because UDP communications
+  # are <i>connectionless,</i> there is no implicit recipient for the packets you
+  # send. Ordinarily you must specify the recipient for each packet you send.
+  # However, EventMachine
+  # provides for the typical pattern of receiving a UDP datagram
+  # from a remote peer, performing some operation, and then sending
+  # one or more packets in response to the same remote peer.
+  # To support this model easily, just use Connection#send_data
+  # in the code that you supply for Connection:receive_data.
+  # EventMachine will
+  # provide an implicit return address for any messages sent to
+  # Connection#send_data within the context of a Connection#receive_data callback,
+  # and your response will automatically go to the correct remote peer.
+  # (TODO: Example-code needed!)
+  #
+  # Observe that the port number that you supply to EventMachine#open_datagram_socket
+  # may be zero. In this case, EventMachine will create a UDP socket
+  # that is bound to an <i>ephemeral</i> (not well-known) port.
+  # This is not appropriate for servers that must publish a well-known
+  # port to which remote peers may send datagrams. But it can be useful
+  # for clients that send datagrams to other servers.
+  # If you do this, you will receive any responses from the remote
+  # servers through the normal Connection#receive_data callback.
+  # Observe that you will probably have issues with firewalls blocking
+  # the ephemeral port numbers, so this technique is most appropriate for LANs.
+  # (TODO: Need an example!)
+  #
+  # If you wish to send datagrams to arbitrary remote peers (not
+  # necessarily ones that have sent data to which you are responding),
+  # then see Connection#send_datagram.
+  #
+  # DO NOT call send_data from a datagram socket
+  # outside of a #receive_data method. Use #send_datagram. If you do use #send_data
+  # outside of a #receive_data method, you'll get a confusing error
+  # because there is no "peer," as #send_data requires. (Inside of #receive_data,
+  # #send_data "fakes" the peer as described above.)
+  #
+  #--
+  # Replaced the implementation on 01Oct06. Thanks to Tobias Gustafsson for pointing
+  # out that this originally did not take a class but only a module.
+  #
+  def self.open_datagram_socket address, port, handler=nil, *args
+    klass = klass_from_handler(Connection, handler, *args)
+    s = open_udp_socket address, port.to_i
+    c = klass.new s, *args
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+
+  # For advanced users. This function sets the default timer granularity, which by default is
+  # slightly smaller than 100 milliseconds. Call this function to set a higher or lower granularity.
+  # The function affects the behavior of #add_timer and #add_periodic_timer. Most applications
+  # will not need to call this function.
+  #
+  # The argument is a number of milliseconds. Avoid setting the quantum to very low values because
+  # that may reduce performance under some extreme conditions. We recommend that you not set a quantum
+  # lower than 10.
+  #
+  # You may only call this function while an EventMachine loop is running (that is, after a call to
+  # EventMachine#run and before a subsequent call to EventMachine#stop).
+  #
+  def self.set_quantum mills
+    set_timer_quantum mills.to_i
+  end
+
+  # Sets the maximum number of timers and periodic timers that may be outstanding at any
+  # given time. You only need to call #set_max_timers if you need more than the default
+  # number of timers, which on most platforms is 1000.
+  # Call this method before calling EventMachine#run.
+  #
+  def self.set_max_timers ct
+    set_max_timer_count ct
+  end
+
+  # Gets the current maximum number of allowed timers
+  #
+  def self.get_max_timers
+    get_max_timer_count
+  end
+
+  # Returns the total number of connections (file descriptors) currently held by the reactor.
+  # Note that a tick must pass after the 'initiation' of a connection for this number to increment.
+  # It's usually accurate, but don't rely on the exact precision of this number unless you really know EM internals.
+  #
+  # For example, $count will be 0 in this case:
+  #
+  #  EM.run {
+  #    EM.connect("rubyeventmachine.com", 80)
+  #    $count = EM.connection_count
+  #  }
+  #
+  # In this example, $count will be 1 since the connection has been established in the next loop of the reactor.
+  #
+  #  EM.run {
+  #    EM.connect("rubyeventmachine.com", 80)
+  #    EM.next_tick {
+  #      $count = EM.connection_count
+  #    }
+  #  }
+  #
+  def self.connection_count
+    self.get_connection_count
+  end
+
+  #--
+  # The is the responder for the loopback-signalled event.
+  # It can be fired either by code running on a separate thread (EM#defer) or on
+  # the main thread (EM#next_tick).
+  # It will often happen that a next_tick handler will reschedule itself. We
+  # consume a copy of the tick queue so that tick events scheduled by tick events
+  # have to wait for the next pass through the reactor core.
+  #
+  def self.run_deferred_callbacks # :nodoc:
+    until (@resultqueue ||= []).empty?
+      result,cback = @resultqueue.pop
+      cback.call result if cback
+    end
+
+    jobs = @next_tick_mutex.synchronize do
+      jobs, @next_tick_queue = @next_tick_queue, []
+      jobs
+    end
+    jobs.each { |j| j.call }
+  end
+
+
+  # #defer is for integrating blocking operations into EventMachine's control flow.
+  # Call #defer with one or two blocks, as shown below (the second block is <i>optional</i>):
+  #
+  #  operation = proc {
+  #    # perform a long-running operation here, such as a database query.
+  #    "result" # as usual, the last expression evaluated in the block will be the return value.
+  #  }
+  #  callback = proc {|result|
+  #    # do something with result here, such as send it back to a network client.
+  #  }
+  #
+  #  EventMachine.defer( operation, callback )
+  #
+  # The action of #defer is to take the block specified in the first parameter (the "operation")
+  # and schedule it for asynchronous execution on an internal thread pool maintained by EventMachine.
+  # When the operation completes, it will pass the result computed by the block (if any)
+  # back to the EventMachine reactor. Then, EventMachine calls the block specified in the
+  # second parameter to #defer (the "callback"), as part of its normal, synchronous
+  # event handling loop. The result computed by the operation block is passed as a parameter
+  # to the callback. You may omit the callback parameter if you don't need to execute any code
+  # after the operation completes.
+  #
+  # == Caveats
+  # Note carefully that the code in your deferred operation will be executed on a separate
+  # thread from the main EventMachine processing and all other Ruby threads that may exist in
+  # your program. Also, multiple deferred operations may be running at once! Therefore, you
+  # are responsible for ensuring that your operation code is threadsafe. [Need more explanation
+  # and examples.]
+  # Don't write a deferred operation that will block forever. If so, the current implementation will
+  # not detect the problem, and the thread will never be returned to the pool. EventMachine limits
+  # the number of threads in its pool, so if you do this enough times, your subsequent deferred
+  # operations won't get a chance to run. [We might put in a timer to detect this problem.]
+  #
+  #--
+  # OBSERVE that #next_tick hacks into this mechanism, so don't make any changes here
+  # without syncing there.
+  #
+  # Running with $VERBOSE set to true gives a warning unless all ivars are defined when
+  # they appear in rvalues. But we DON'T ever want to initialize @threadqueue unless we
+  # need it, because the Ruby threads are so heavyweight. We end up with this bizarre
+  # way of initializing @threadqueue because EventMachine is a Module, not a Class, and
+  # has no constructor.
+  #
+  def self.defer op = nil, callback = nil, &blk
+    unless @threadpool
+      require 'thread'
+      @threadpool = []
+      @threadqueue = ::Queue.new
+      @resultqueue = ::Queue.new
+      spawn_threadpool
+    end
+
+    @threadqueue << [op||blk,callback]
+  end
+
+  def self.spawn_threadpool # :nodoc:
+    until @threadpool.size == @threadpool_size.to_i
+      thread = Thread.new do
+        while true
+          op, cback = *@threadqueue.pop
+          result = op.call
+          @resultqueue << [result, cback]
+          EventMachine.signal_loopbreak
+        end
+      end
+      @threadpool << thread
+    end
+  end
+
+  class << self
+    attr_reader :threadpool # :nodoc:
+
+    # Size of the EventMachine.defer threadpool (defaults to 20)
+    attr_accessor :threadpool_size
+    EventMachine.threadpool_size = 20
+  end
+
+  # Schedules a proc for execution immediately after the next "turn" through the reactor
+  # core. An advanced technique, this can be useful for improving memory management and/or
+  # application responsiveness, especially when scheduling large amounts of data for
+  # writing to a network connection. TODO, we need a FAQ entry on this subject.
+  #
+  # #next_tick takes either a single argument (which must be a Proc) or a block.
+  #--
+  # This works by adding to the @resultqueue that's used for #defer.
+  # The general idea is that next_tick is used when we want to give the reactor a chance
+  # to let other operations run, either to balance the load out more evenly, or to let
+  # outbound network buffers drain, or both. So we probably do NOT want to block, and
+  # we probably do NOT want to be spinning any threads. A program that uses next_tick
+  # but not #defer shouldn't suffer the penalty of having Ruby threads running. They're
+  # extremely expensive even if they're just sleeping.
+  #
+  def self.next_tick pr=nil, &block
+    raise ArgumentError, "no proc or block given" unless ((pr && pr.respond_to?(:call)) or block)
+    @next_tick_mutex.synchronize do
+      (@next_tick_queue ||= []) << ( pr || block )
+    end
+    signal_loopbreak if reactor_running?
+  end
+
+  # A wrapper over the setuid system call. Particularly useful when opening a network
+  # server on a privileged port because you can use this call to drop privileges
+  # after opening the port. Also very useful after a call to #set_descriptor_table_size,
+  # which generally requires that you start your process with root privileges.
+  #
+  # This method has no effective implementation on Windows or in the pure-Ruby
+  # implementation of EventMachine.
+  # Call #set_effective_user by passing it a string containing the effective name
+  # of the user whose privilege-level your process should attain.
+  # This method is intended for use in enforcing security requirements, consequently
+  # it will throw a fatal error and end your program if it fails.
+  #
+  def self.set_effective_user username
+    EventMachine::setuid_string username
+  end
+
+
+  # Sets the maximum number of file or socket descriptors that your process may open.
+  # You can pass this method an integer specifying the new size of the descriptor table.
+  # Returns the new descriptor-table size, which may be less than the number you
+  # requested. If you call this method with no arguments, it will simply return
+  # the current size of the descriptor table without attempting to change it.
+  #
+  # The new limit on open descriptors ONLY applies to sockets and other descriptors
+  # that belong to EventMachine. It has NO EFFECT on the number of descriptors
+  # you can create in ordinary Ruby code.
+  #
+  # Not available on all platforms. Increasing the number of descriptors beyond its
+  # default limit usually requires superuser privileges. (See #set_effective_user
+  # for a way to drop superuser privileges while your program is running.)
+  #
+  def self.set_descriptor_table_size n_descriptors=nil
+    EventMachine::set_rlimit_nofile n_descriptors
+  end
+
+
+
+  # Run an external process. This does not currently work on Windows.
+  #
+  #  module RubyCounter
+  #    def post_init
+  #      # count up to 5
+  #      send_data "5\n"
+  #    end
+  #    def receive_data data
+  #      puts "ruby sent me: #{data}"
+  #    end
+  #    def unbind
+  #      puts "ruby died with exit status: #{get_status.exitstatus}"
+  #    end
+  #  end
+  #
+  #  EM.run{
+  #    EM.popen("ruby -e' $stdout.sync = true; gets.to_i.times{ |i| puts i+1; sleep 1 } '", RubyCounter)
+  #  }
+  #
+  # Also see EventMachine::DeferrableChildProcess and EventMachine.system
+  #--
+  # At this moment, it's only available on Unix.
+  # Perhaps misnamed since the underlying function uses socketpair and is full-duplex.
+  #
+  def self.popen cmd, handler=nil, *args
+    klass = klass_from_handler(Connection, handler, *args)
+    w = Shellwords::shellwords( cmd )
+    w.unshift( w.first ) if w.first
+    s = invoke_popen( w )
+    c = klass.new s, *args
+    @conns[s] = c
+    yield(c) if block_given?
+    c
+  end
+
+
+  # Tells you whether the EventMachine reactor loop is currently running. Returns true or
+  # false. Useful when writing libraries that want to run event-driven code, but may
+  # be running in programs that are already event-driven. In such cases, if EventMachine#reactor_running?
+  # returns false, your code can invoke EventMachine#run and run your application code inside
+  # the block passed to that method. If EventMachine#reactor_running? returns true, just
+  # execute your event-aware code.
+  #
+  # This method is necessary because calling EventMachine#run inside of another call to
+  # EventMachine#run generates a fatal error.
+  #
+  def self.reactor_running?
+    (@reactor_running || false)
+  end
+
+
+  # (Experimental)
+  #
+  #
+  def self.open_keyboard handler=nil, *args
+    klass = klass_from_handler(Connection, handler, *args)
+
+    s = read_keyboard
+    c = klass.new s, *args
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+  # EventMachine's file monitoring API. Currently supported are the following events
+  # on individual files, using inotify on Linux systems, and kqueue for OSX/BSD:
+  #
+  # * File modified (written to)
+  # * File moved/renamed
+  # * File deleted
+  #
+  # EventMachine::watch_file takes a filename and a handler Module containing your custom callback methods.
+  # This will setup the low level monitoring on the specified file, and create a new EventMachine::FileWatch
+  # object with your Module mixed in. FileWatch is a subclass of EM::Connection, so callbacks on this object
+  # work in the familiar way. The callbacks that will be fired by EventMachine are:
+  #
+  # * file_modified
+  # * file_moved
+  # * file_deleted
+  #
+  # You can access the filename being monitored from within this object using FileWatch#path.
+  #
+  # When a file is deleted, FileWatch#stop_watching will be called after your file_deleted callback, 
+  # to clean up the underlying monitoring and remove EventMachine's reference to the now-useless FileWatch.
+  # This will in turn call unbind, if you wish to use it.
+  #
+  # The corresponding system-level Errno will be raised when attempting to monitor non-existent files,
+  # files with wrong permissions, or if an error occurs dealing with inotify/kqueue.
+  #
+  # === Usage example:
+  #
+  #  Make sure we have a file to monitor:
+  #  $ echo "bar" > /tmp/foo
+  #
+  #  module Handler
+  #    def file_modified
+  #      puts "#{path} modified"
+  #    end
+  #
+  #    def file_moved
+  #      puts "#{path} moved"
+  #    end
+  #
+  #    def file_deleted
+  #      puts "#{path} deleted"
+  #    end
+  #
+  #    def unbind
+  #      puts "#{path} monitoring ceased"
+  #    end
+  #  end
+  #
+  #  EM.kqueue = true if EM.kqueue? # file watching requires kqueue on OSX
+  #
+  #  EM.run {
+  #    EM.watch_file("/tmp/foo", Handler)
+  #  }
+  #
+  #  $ echo "baz" >> /tmp/foo    =>    "/tmp/foo modified"
+  #  $ mv /tmp/foo /tmp/oof      =>    "/tmp/foo moved"
+  #  $ rm /tmp/oof               =>    "/tmp/foo deleted"
+  #                              =>    "/tmp/foo monitoring ceased"
+  #
+  # Note that we have not implemented the ability to pick up on the new filename after a rename.
+  # Calling #path will always return the filename you originally used.
+  #
+  def self.watch_file(filename, handler=nil, *args)
+    klass = klass_from_handler(FileWatch, handler, *args)
+
+    s = EM::watch_filename(filename)
+    c = klass.new s, *args
+    # we have to set the path like this because of how Connection.new works
+    c.instance_variable_set("@path", filename)
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+  # EventMachine's process monitoring API. Currently supported using kqueue for OSX/BSD.
+  #
+  # === Usage example:
+  #
+  #  module ProcessWatcher
+  #    def process_exited
+  #      put 'the forked child died!'
+  #    end
+  #  end
+  #
+  #  pid = fork{ sleep }
+  #
+  #  EM.run{
+  #    EM.watch_process(pid, ProcessWatcher)
+  #    EM.add_timer(1){ Process.kill('TERM', pid) }
+  #  }
+  #
+  def self.watch_process(pid, handler=nil, *args)
+    pid = pid.to_i
+
+    klass = klass_from_handler(ProcessWatch, handler, *args)
+
+    s = EM::watch_pid(pid)
+    c = klass.new s, *args
+    # we have to set the path like this because of how Connection.new works
+    c.instance_variable_set("@pid", pid)
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+  # Catch-all for errors raised during event loop callbacks.
+  #
+  #  EM.error_handler{ |e|
+  #    puts "Error raised during event loop: #{e.message}"
+  #  }
+  #
+  def self.error_handler cb = nil, &blk
+    if cb or blk
+      @error_handler = cb || blk
+    elsif instance_variable_defined? :@error_handler
+      remove_instance_variable :@error_handler
+    end
+  end
+
+  # enable_proxy allows for direct writing of incoming data back out to another descriptor, at the C++ level in the reactor.
+  # This is especially useful for proxies where high performance is required. Propogating data from a server response
+  # all the way up to Ruby, and then back down to the reactor to be sent back to the client, is often unnecessary and
+  # incurs a significant performance decrease.
+  #
+  # The two arguments are Connections, 'from' and 'to'. 'from' is the connection whose inbound data you want
+  # relayed back out. 'to' is the connection to write it to.
+  #
+  # Once you call this method, the 'from' connection will no longer get receive_data callbacks from the reactor,
+  # except in the case that 'to' connection has already closed when attempting to write to it. You can see
+  # in the example, that proxy_target_unbound will be called when this occurs. After that, further incoming
+  # data will be passed into receive_data as normal.
+  #
+  # Note also that this feature supports different types of descriptors - TCP, UDP, and pipes. You can relay
+  # data from one kind to another.
+  #
+  # Example:
+  #
+  #  module ProxyConnection
+  #    def initialize(client, request)
+  #      @client, @request = client, request
+  #    end
+  #
+  #    def post_init
+  #      EM::enable_proxy(self, @client)
+  #    end
+  #
+  #    def connection_completed
+  #      send_data @request
+  #    end
+  #
+  #    def proxy_target_unbound
+  #      close_connection
+  #    end
+  #
+  #    def unbind
+  #      @client.close_connection_after_writing
+  #    end
+  #  end
+  #
+  #  module ProxyServer
+  #    def receive_data(data)
+  #      (@buf ||= "") << data
+  #      if @buf =~ /\r\n\r\n/ # all http headers received
+  #        EM.connect("10.0.0.15", 80, ProxyConnection, self, data)
+  #      end
+  #    end
+  #  end
+  #
+  #  EM.run {
+  #    EM.start_server("127.0.0.1", 8080, ProxyServer)
+  #  }
+  def self.enable_proxy(from, to, bufsize=0)
+    EM::start_proxy(from.signature, to.signature, bufsize)
+  end
+
+  # disable_proxy takes just one argument, a Connection that has proxying enabled via enable_proxy.
+  # Calling this method will remove that functionality and your connection will begin receiving
+  # data via receive_data again.
+  def self.disable_proxy(from)
+    EM::stop_proxy(from.signature)
+  end
+
+  # Retrieve the heartbeat interval. This is how often EventMachine will check for dead connections
+  # that have had an InactivityTimeout set via Connection#set_comm_inactivity_timeout.
+  # Default is 2 seconds.
+  def self.heartbeat_interval
+    EM::get_heartbeat_interval
+  end
+
+  # Set the heartbeat interval. This is how often EventMachine will check for dead connections
+  # that have had an InactivityTimeout set via Connection#set_comm_inactivity_timeout.
+  # Takes a Numeric number of seconds. Default is 2.
+  def self.heartbeat_interval= (time)
+    EM::set_heartbeat_interval time.to_f
+  end
+
+  private
+
+  def self.event_callback conn_binding, opcode, data # :nodoc:
+    #
+    # Changed 27Dec07: Eliminated the hookable error handling.
+    # No one was using it, and it degraded performance significantly.
+    # It's in original_event_callback, which is dead code.
+    #
+    # Changed 25Jul08: Added a partial solution to the problem of exceptions
+    # raised in user-written event-handlers. If such exceptions are not caught,
+    # we must cause the reactor to stop, and then re-raise the exception.
+    # Otherwise, the reactor doesn't stop and it's left on the call stack.
+    # This is partial because we only added it to #unbind, where it's critical
+    # (to keep unbind handlers from being re-entered when a stopping reactor
+    # runs down open connections). It should go on the other calls to user
+    # code, but the performance impact may be too large.
+    #
+    if opcode == ConnectionUnbound
+      if c = @conns.delete( conn_binding )
+        begin
+          c.unbind
+        rescue
+          @wrapped_exception = $!
+          stop
+        end
+      elsif c = @acceptors.delete( conn_binding )
+        # no-op
+      else
+        raise ConnectionNotBound, "recieved ConnectionUnbound for an unknown signature: #{conn_binding}"
+      end
+    elsif opcode == ConnectionAccepted
+      accep,args,blk = @acceptors[conn_binding]
+      raise NoHandlerForAcceptedConnection unless accep
+      c = accep.new data, *args
+      @conns[data] = c
+      blk and blk.call(c)
+      c # (needed?)
+    elsif opcode == ConnectionCompleted
+      c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
+      c.connection_completed
+    ##
+    # The remaining code is a fallback for the pure ruby and java reactors.
+    # In the C++ reactor, these events are handled in the C event_callback() in rubymain.cpp
+    elsif opcode == TimerFired
+      t = @timers.delete( data )
+      return if t == false # timer cancelled
+      t or raise UnknownTimerFired, "timer data: #{data}"
+      t.call
+    elsif opcode == ConnectionData
+      c = @conns[conn_binding] or raise ConnectionNotBound, "received data #{data} for unknown signature: #{conn_binding}"
+      c.receive_data data
+    elsif opcode == LoopbreakSignalled
+      run_deferred_callbacks
+    elsif opcode == ConnectionNotifyReadable
+      c = @conns[conn_binding] or raise ConnectionNotBound
+      c.notify_readable
+    elsif opcode == ConnectionNotifyWritable
+      c = @conns[conn_binding] or raise ConnectionNotBound
+      c.notify_writable
+    end
+  end
+
+  #--
+  # The original event_callback below handled runtime errors in ruby and degraded performance significantly.
+  # An optional C-based error handler is now available via EM::error_handler
+  #
+  # private
+  # def EventMachine::original_event_callback conn_binding, opcode, data
+  #   #
+  #   # Added 03Oct07: Any code path that invokes user-written code must
+  #   # wrap itself in a begin/rescue for RuntimeErrors, that calls the
+  #   # user-overridable class method #handle_runtime_error.
+  #   #
+  #   if opcode == ConnectionData
+  #     c = @conns[conn_binding] or raise ConnectionNotBound
+  #     begin
+  #       c.receive_data data
+  #     rescue
+  #       EventMachine.handle_runtime_error
+  #     end
+  #   elsif opcode == ConnectionUnbound
+  #     if c = @conns.delete( conn_binding )
+  #       begin
+  #         c.unbind
+  #       rescue
+  #         EventMachine.handle_runtime_error
+  #       end
+  #     elsif c = @acceptors.delete( conn_binding )
+  #       # no-op
+  #     else
+  #       raise ConnectionNotBound
+  #     end
+  #   elsif opcode == ConnectionAccepted
+  #     accep,args,blk = @acceptors[conn_binding]
+  #     raise NoHandlerForAcceptedConnection unless accep
+  #     c = accep.new data, *args
+  #     @conns[data] = c
+  #     begin
+  #       blk and blk.call(c)
+  #     rescue
+  #       EventMachine.handle_runtime_error
+  #     end
+  #     c # (needed?)
+  #   elsif opcode == TimerFired
+  #     t = @timers.delete( data ) or raise UnknownTimerFired
+  #     begin
+  #       t.call
+  #     rescue
+  #       EventMachine.handle_runtime_error
+  #     end
+  #   elsif opcode == ConnectionCompleted
+  #     c = @conns[conn_binding] or raise ConnectionNotBound
+  #     begin
+  #       c.connection_completed
+  #     rescue
+  #       EventMachine.handle_runtime_error
+  #     end
+  #   elsif opcode == LoopbreakSignalled
+  #     begin
+  #     run_deferred_callbacks
+  #     rescue
+  #       EventMachine.handle_runtime_error
+  #     end
+  #   end
+  # end
+  #
+  #
+  # # Default handler for RuntimeErrors that are raised in user code.
+  # # The default behavior is to re-raise the error, which ends your program.
+  # # To override the default behavior, re-implement this method in your code.
+  # # For example:
+  # #
+  # #  module EventMachine
+  # #    def self.handle_runtime_error
+  # #      $>.puts $!
+  # #    end
+  # #  end
+  # #
+  # #--
+  # # We need to ensure that any code path which invokes user code rescues RuntimeError
+  # # and calls this method. The obvious place to do that is in #event_callback,
+  # # but, scurrilously, it turns out that we need to be finer grained that that.
+  # # Periodic timers, in particular, wrap their invocations of user code inside
+  # # procs that do other stuff we can't not do, like schedule the next invocation.
+  # # This is a potential non-robustness, since we need to remember to hook in the
+  # # error handler whenever and wherever we change how user code is invoked.
+  # #
+  # def EventMachine::handle_runtime_error
+  #   @runtime_error_hook ? @runtime_error_hook.call : raise
+  # end
+  #
+  # # Sets a handler for RuntimeErrors that are raised in user code.
+  # # Pass a block with no parameters. You can also call this method without a block,
+  # # which restores the default behavior (see #handle_runtime_error).
+  # #
+  # def EventMachine::set_runtime_error_hook &blk
+  #   @runtime_error_hook = blk
+  # end
+
+  #--
+  # This is a provisional implementation of a stream-oriented file access object.
+  # We also experiment with wrapping up some better exception reporting.
+  def self._open_file_for_writing filename, handler=nil # :nodoc:
+    klass = klass_from_handler(Connection, handler)
+
+    s = _write_file filename
+    c = klass.new s
+    @conns[s] = c
+    block_given? and yield c
+    c
+  end
+
+  private
+  def self.klass_from_handler(klass = Connection, handler = nil, *args)
+    klass = if handler and handler.is_a?(Class)
+      raise ArgumentError, "must provide module or subclass of #{klass.name}" unless klass >= handler
+      handler
+    elsif handler
+      Class.new(klass){ include handler }
+    else
+      klass
+    end
+
+    arity = klass.instance_method(:initialize).arity
+    expected = arity >= 0 ? arity : -(arity + 1)
+    if (arity >= 0 and args.size != expected) or (arity < 0 and args.size < expected)
+      raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size} for #{expected})"
+    end
+
+    klass
+  end
+end # module EventMachine
+
+# Save everyone some typing.
+EM = EventMachine
+EM::P = EventMachine::Protocols
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma.rb
new file mode 100644 (file)
index 0000000..41ef698
--- /dev/null
@@ -0,0 +1,32 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 Apr 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#-------------------------------------------------------------------\r
+#\r
+\r
+\r
+require 'rubyeventmachine'\r
+require 'evma/reactor'\r
+require 'evma/callback'\r
+require 'evma/protocol'\r
+require 'evma/factory'\r
+require 'evma/container'\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/callback.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/callback.rb
new file mode 100644 (file)
index 0000000..405cdf3
--- /dev/null
@@ -0,0 +1,32 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 19 May 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+\r
+module EventMachine\r
+\r
+def self.event_callback target, opcode, data\r
+  Evma::Container.callback target, opcode, data\r
+end\r
+\r
+end # module EventMachine\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/container.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/container.rb
new file mode 100644 (file)
index 0000000..6fb7eee
--- /dev/null
@@ -0,0 +1,75 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 19 May 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+\r
+\r
+require 'singleton'\r
+\r
+module Evma\r
+\r
+class ContainerHasObject < Exception; end\r
+class UnsupportedCallback < Exception; end\r
+class UnknownTarget < Exception; end\r
+\r
+class Container\r
+  include Singleton\r
+\r
+  def initialize\r
+    @objects = {}\r
+  end\r
+\r
+  def self.store obj\r
+    instance.store obj\r
+  end\r
+\r
+  def self.callback target, opcode, data\r
+    instance.callback target, opcode, data\r
+  end\r
+\r
+  def store obj\r
+    sig = obj.signature\r
+    raise ContainerHasObject.new(sig) if @objects.has_key?(sig)\r
+    @objects[sig] = obj\r
+  end\r
+\r
+  def callback target, opcode, data\r
+    case opcode\r
+    when 101 # received data\r
+      obj = @objects[target] or raise UnknownTarget.new( target )\r
+      obj.receive_data data\r
+    when 102 # unbind\r
+      obj = @objects[target] or raise UnknownTarget.new( target )\r
+      obj.unbind\r
+      @objects.delete obj.signature\r
+    when 103 # accept\r
+      obj = @objects[target] or raise UnknownTarget.new( target )\r
+      obj.accept data\r
+    else\r
+      raise UnsupportedCallback.new( opcode.to_s )\r
+    end\r
+  end\r
+\r
+end # class Container\r
+end # module Evma\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/factory.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/factory.rb
new file mode 100644 (file)
index 0000000..ef12179
--- /dev/null
@@ -0,0 +1,77 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 19 May 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+\r
+module Evma\r
+class ProtocolFactory < Protocol\r
+\r
+  #--\r
+  # default implementation raises an exception.\r
+  # we expect subclasses to override this.\r
+  # we can't do anything reasonable here because\r
+  def accept new_object\r
+    # don't bother calling Evma::Reactor.instance, since only Reactor can call accept\r
+    Evma::Container.store Evma::Protocol.new( new_object )\r
+    EventMachine.close_connection new_object, false\r
+  end\r
+\r
+\r
+end # class ProtocolFactory\r
+end # module Evma\r
+\r
+######################################\r
+\r
+module Evma\r
+class TcpSocket\r
+\r
+  def self.connect server, port, protocol_handler = Evma::Protocol\r
+    Evma::Reactor.instance # ensure initialization\r
+    sig = EventMachine.connect_server server, port\r
+    Evma::Container.store protocol_handler.new( sig )\r
+  end\r
+\r
+end\r
+end # module Evma\r
+\r
+######################################\r
+\r
+module Evma\r
+class TcpServerFactory < Evma::ProtocolFactory\r
+\r
+  def initialize server, port, protocol_handler = Evma::Protocol\r
+    Evma::Reactor.instance # ensure initialization\r
+    sig = EventMachine.start_tcp_server server, port\r
+    super sig\r
+    @protocol_handler = protocol_handler\r
+    Evma::Container.store self\r
+  end\r
+\r
+  def accept new_obj\r
+    # don't bother calling Evma::Reactor.instance, since only Reactor can call accept\r
+    Evma::Container.store @protocol_handler.new( new_obj )\r
+  end\r
+\r
+end\r
+end # module Evma\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/protocol.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/protocol.rb
new file mode 100644 (file)
index 0000000..604bad2
--- /dev/null
@@ -0,0 +1,87 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 19 May 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+\r
+module Evma\r
+class Protocol\r
+\r
+  attr_reader :signature\r
+\r
+  def initialize sig\r
+    @signature = sig\r
+  end\r
+\r
+  def unbind\r
+  end\r
+\r
+  def close\r
+    Evma::Reactor.instance # ensure initialized\r
+    EventMachine.close_connection signature, false\r
+  end\r
+\r
+  def close_after_writing\r
+    Evma::Reactor.instance # ensure initialized\r
+    EventMachine.close_connection signature, true\r
+  end\r
+\r
+end # class Protocol\r
+end # module Evma\r
+\r
+\r
+###########################################\r
+\r
+module Evma\r
+class StreamProtocol < Protocol\r
+\r
+  def initialize sig\r
+    super\r
+  end\r
+\r
+  def send_data data\r
+    Evma::Reactor.instance # ensure initialized\r
+    EventMachine.send_data signature, data, data.length\r
+  end\r
+\r
+end # class Protocol\r
+end # module Evma\r
+\r
+\r
+###########################################\r
+\r
+module Evma\r
+class DatagramProtocol < Protocol\r
+\r
+  def initialize sig\r
+    super\r
+  end\r
+\r
+  def send_message data\r
+    Evma::Reactor.instance # ensure initialized\r
+    raise "unimplemented"\r
+  end\r
+\r
+end # class Protocol\r
+end # module Evma\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/reactor.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/evma/reactor.rb
new file mode 100644 (file)
index 0000000..07d92e7
--- /dev/null
@@ -0,0 +1,48 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 18 May 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+\r
+\r
+\r
+require 'singleton'\r
+\r
+module Evma\r
+class Reactor\r
+  include Singleton\r
+\r
+  #--\r
+  def initialize\r
+    EventMachine.initialize_event_machine\r
+  end\r
+\r
+  #--\r
+  #\r
+  def run\r
+    EventMachine.run_machine\r
+  end\r
+\r
+end # class Reactor\r
+end # module Evma\r
+\r
+\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/jeventmachine.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/jeventmachine.rb
new file mode 100644 (file)
index 0000000..28cecb1
--- /dev/null
@@ -0,0 +1,257 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 Apr 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+# 
+
+# This module provides "glue" for the Java version of the EventMachine reactor core.
+# For C++ EventMachines, the analogous functionality is found in ext/rubymain.cpp,
+# which is a garden-variety Ruby-extension glue module.
+
+require 'java'
+require 'em_reactor'
+require 'socket'
+
+java_import java.io.FileDescriptor
+java_import java.nio.channels.SocketChannel
+java_import java.lang.reflect.Field
+
+module JavaFields
+  def set_field(key, value)
+    field = getClass.getDeclaredField(key)
+    field.setAccessible(true)
+    if field.getType.toString == 'int'
+      field.setInt(self, value)
+    else
+      field.set(self, value)
+    end
+  end
+
+  def get_field(key)
+    field = getClass.getDeclaredField(key)
+    field.setAccessible(true)
+    field.get(self)
+  end
+end
+
+FileDescriptor.send :include, JavaFields
+SocketChannel.send :include, JavaFields
+
+module EventMachine
+  # TODO: These event numbers are defined in way too many places.
+  # DRY them up.
+  TimerFired = 100
+  ConnectionData = 101
+  ConnectionUnbound = 102
+  ConnectionAccepted = 103
+  ConnectionCompleted = 104
+  LoopbreakSignalled = 105
+  ConnectionNotifyReadable = 106
+  ConnectionNotifyWritable = 107
+  SslHandshakeCompleted = 108
+
+  # Exceptions that are defined in rubymain.cpp
+  class ConnectionNotBound < RuntimeError; end
+  class UnknownTimerFired < RuntimeError; end
+  class Unsupported < RuntimeError; end
+
+  # This thunk class used to be called EM, but that caused conflicts with
+  # the alias "EM" for module EventMachine. (FC, 20Jun08)
+  class JEM < com.rubyeventmachine.EmReactor
+    def eventCallback a1, a2, a3, a4
+      s = String.from_java_bytes(a3.array[a3.position...a3.limit]) if a3
+      EventMachine::event_callback a1, a2, s || a4
+      nil
+    end
+  end
+  # class Connection < com.rubyeventmachine.Connection
+  #   def associate_callback_target sig
+  #     # No-op for the time being.
+  #   end
+  # end
+  def self.initialize_event_machine
+    @em = JEM.new
+  end
+  def self.release_machine
+    @em = nil
+  end
+  def self.add_oneshot_timer interval
+    @em.installOneshotTimer interval
+  end
+  def self.run_machine
+    @em.run
+  end
+  def self.stop
+    @em.stop
+  end
+  def self.start_tcp_server server, port
+    @em.startTcpServer server, port
+  end
+  def self.stop_tcp_server sig
+    @em.stopTcpServer sig
+  end
+  def self.start_unix_server filename
+    # TEMPORARILY unsupported until someone figures out how to do it.
+    raise "unsupported on this platform"
+  end
+  def self.send_data sig, data, length
+    @em.sendData sig, data.to_java_bytes
+  end
+  def self.send_datagram sig, data, length, address, port
+    @em.sendDatagram sig, data, length, address, port
+  end
+  def self.connect_server server, port
+    bind_connect_server nil, nil, server, port
+  end
+  def self.bind_connect_server bind_addr, bind_port, server, port
+    @em.connectTcpServer bind_addr, bind_port.to_i, server, port
+  end
+  def self.close_connection sig, after_writing
+    @em.closeConnection sig, after_writing
+  end
+  def self.set_comm_inactivity_timeout sig, interval
+    @em.setCommInactivityTimeout sig, interval
+  end
+  def self.start_tls sig
+    @em.startTls sig
+  end
+  def self.ssl?
+    false
+  end
+  def self.signal_loopbreak
+    @em.signalLoopbreak
+  end
+  def self.set_timer_quantum q
+    @em.setTimerQuantum q
+  end
+  def self.epoll
+    # Epoll is a no-op for Java.
+    # The latest Java versions run epoll when possible in NIO.
+  end
+  def self.epoll= val
+  end
+  def self.kqueue
+  end
+  def self.kqueue= val
+  end
+  def self.epoll?
+    false
+  end
+  def self.kqueue?
+    false
+  end
+  def self.set_rlimit_nofile n_descriptors
+    # Currently a no-op for Java.
+  end
+  def self.open_udp_socket server, port
+    @em.openUdpSocket server, port
+  end
+  def self.invoke_popen cmd
+    # TEMPORARILY unsupported until someone figures out how to do it.
+    raise "unsupported on this platform"
+  end
+  def self.read_keyboard
+    # TEMPORARILY unsupported until someone figures out how to do it.
+    raise "temporarily unsupported on this platform"
+  end
+  def self.set_max_timer_count num
+    # harmless no-op in Java. There's no built-in timer limit.
+    @max_timer_count = num
+  end
+  def self.get_max_timer_count
+    # harmless no-op in Java. There's no built-in timer limit.
+    @max_timer_count || 100_000
+  end
+  def self.library_type
+    :java
+  end
+  def self.get_peername sig
+    if peer = @em.getPeerName(sig)
+      Socket.pack_sockaddr_in *peer
+    end
+  end
+  def self.attach_fd fileno, watch_mode
+    # 3Aug09: We could pass in the actual SocketChannel, but then it would be modified (set as non-blocking), and
+    # we would need some logic to make sure detach_fd below didn't clobber it. For now, we just always make a new
+    # SocketChannel for the underlying file descriptor
+    # if fileno.java_kind_of? SocketChannel
+    #   ch = fileno
+    #   ch.configureBlocking(false)
+    #   fileno = nil
+    # elsif fileno.java_kind_of? java.nio.channels.Channel
+
+    if fileno.java_kind_of? java.nio.channels.Channel
+      field = fileno.getClass.getDeclaredField('fdVal')
+      field.setAccessible(true)
+      fileno = field.get(fileno)
+    else
+      raise ArgumentError, 'attach_fd requires Java Channel or POSIX fileno' unless fileno.is_a? Fixnum
+    end
+
+    if fileno == 0
+      raise "can't open STDIN as selectable in Java =("
+    elsif fileno.is_a? Fixnum
+      # 8Aug09: The following code is specific to the sun jvm's SocketChannelImpl. Is there a cross-platform
+      # way of implementing this? If so, also remember to update EventableSocketChannel#close and #cleanup
+      fd = FileDescriptor.new
+      fd.set_field 'fd', fileno
+
+      ch = SocketChannel.open
+      ch.configureBlocking(false)
+      ch.kill
+      ch.set_field 'fd', fd
+      ch.set_field 'fdVal', fileno
+      ch.set_field 'state', ch.get_field('ST_CONNECTED')
+    end
+
+    @em.attachChannel(ch,watch_mode)
+  end
+  def self.detach_fd sig
+    if ch = @em.detachChannel(sig)
+      ch.get_field 'fdVal'
+    end
+  end
+
+  def self.set_notify_readable sig, mode
+    @em.setNotifyReadable(sig, mode)
+  end
+  def self.set_notify_writable sig, mode
+    @em.setNotifyWritable(sig, mode)
+  end
+
+  def self.is_notify_readable sig
+    @em.isNotifyReadable(sig)
+  end
+  def self.is_notify_writable sig
+    @em.isNotifyWritable(sig)
+  end
+  def self.get_connection_count
+    @em.getConnectionCount
+  end
+
+  class Connection
+    def associate_callback_target sig
+      # No-op for the time being
+    end
+  end
+end
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/pr_eventmachine.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/lib/pr_eventmachine.rb
new file mode 100644 (file)
index 0000000..9c41629
--- /dev/null
@@ -0,0 +1,1022 @@
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 Apr 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#-------------------------------------------------------------------\r
+#\r
+# \r
+\r
+# TODO List:\r
+# TCP-connects currently assume non-blocking connect is available- need to\r
+#  degrade automatically on versions of Ruby prior to June 2006.\r
+#\r
+\r
+require 'singleton'\r
+require 'forwardable'\r
+require 'socket'\r
+require 'fcntl'\r
+require 'set'\r
+\r
+\r
+module EventMachine\r
+\r
+\r
+  class << self\r
+    # This is mostly useful for automated tests.\r
+    # Return a distinctive symbol so the caller knows whether he's dealing\r
+    # with an extension or with a pure-Ruby library.\r
+    def library_type\r
+      :pure_ruby\r
+    end\r
+\r
+    # #initialize_event_machine\r
+    def initialize_event_machine\r
+      Reactor.instance.initialize_for_run\r
+    end\r
+\r
+    # #add_oneshot_timer\r
+    #--\r
+    # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby\r
+    # processor still wants them in seconds.\r
+    def add_oneshot_timer interval\r
+      Reactor.instance.install_oneshot_timer(interval / 1000)\r
+    end\r
+\r
+    # run_machine\r
+    def run_machine\r
+      Reactor.instance.run\r
+    end\r
+\r
+    # release_machine. Probably a no-op.\r
+    def release_machine\r
+    end\r
+\r
+    # #stop\r
+    def stop\r
+      Reactor.instance.stop\r
+    end\r
+\r
+    # #connect_server. Return a connection descriptor to the caller.\r
+    # TODO, what do we return here if we can't connect?\r
+    def connect_server host, port\r
+      bind_connect_server nil, nil, host, port\r
+    end\r
+\r
+    def bind_connect_server bind_addr, bind_port, host, port\r
+      EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid\r
+    end\r
+\r
+    # #send_data\r
+    def send_data target, data, datalength\r
+      selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"\r
+      selectable.send_data data\r
+    end\r
+\r
+    # #close_connection\r
+    # The extension version does NOT raise any kind of an error if an attempt is made\r
+    # to close a non-existent connection. Not sure whether we should. For now, we'll\r
+    # raise an error here in that case.\r
+    def close_connection target, after_writing\r
+      selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target"\r
+      selectable.schedule_close after_writing\r
+    end\r
+\r
+    # #start_tcp_server\r
+    def start_tcp_server host, port\r
+      (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"\r
+      s.uuid\r
+    end\r
+\r
+    # #stop_tcp_server\r
+    def stop_tcp_server sig\r
+      s = Reactor.instance.get_selectable(sig)\r
+      s.schedule_close\r
+    end\r
+\r
+    # #start_unix_server\r
+    def start_unix_server chain\r
+      (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"\r
+      s.uuid\r
+    end\r
+\r
+    # #connect_unix_server\r
+    def connect_unix_server chain\r
+      EvmaUNIXClient.connect(chain).uuid\r
+    end\r
+\r
+    # #signal_loopbreak\r
+    def signal_loopbreak\r
+      Reactor.instance.signal_loopbreak\r
+    end\r
+\r
+    # #get_peername\r
+    def get_peername sig\r
+      selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"\r
+      selectable.get_peername\r
+    end\r
+\r
+    # #open_udp_socket\r
+    def open_udp_socket host, port\r
+      EvmaUDPSocket.create(host, port).uuid\r
+    end\r
+\r
+    # #send_datagram. This is currently only for UDP!\r
+    # We need to make it work with unix-domain sockets as well.\r
+    def send_datagram target, data, datalength, host, port\r
+      selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"\r
+      selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)\r
+    end\r
+\r
+\r
+    # #set_timer_quantum in milliseconds. The underlying Reactor function wants a (possibly\r
+    # fractional) number of seconds.\r
+    def set_timer_quantum interval\r
+      Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)\r
+    end\r
+\r
+    # #epoll is a harmless no-op in the pure-Ruby implementation. This is intended to ensure\r
+    # that user code behaves properly across different EM implementations.\r
+    def epoll\r
+    end\r
+\r
+    # #ssl? is not implemented for pure-Ruby implementation\r
+    def ssl?\r
+      false\r
+    end\r
+\r
+    # #set_rlimit_nofile is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in\r
+    # per-process file-descriptor limit.\r
+    def set_rlimit_nofile n\r
+      1024\r
+    end\r
+\r
+    # #set_max_timer_count is a harmless no-op in pure Ruby, which doesn't have a built-in limit\r
+    # on the number of available timers.\r
+    def set_max_timer_count n\r
+    end\r
+\r
+    # #send_file_data\r
+    def send_file_data sig, filename\r
+      sz = File.size(filename)\r
+      raise "file too large" if sz > 32*1024\r
+      data =\r
+      begin\r
+        File.read filename\r
+      rescue\r
+        ""\r
+      end\r
+      send_data sig, data, data.length\r
+    end\r
+\r
+    # #get_outbound_data_size\r
+    #\r
+    def get_outbound_data_size sig\r
+      r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"\r
+      r.get_outbound_data_size\r
+    end\r
+\r
+    # #read_keyboard\r
+    #\r
+    def read_keyboard\r
+      EvmaKeyboard.open.uuid\r
+    end\r
+\r
+    # #set_comm_inactivity_timeout\r
+    #\r
+    def set_comm_inactivity_timeout sig, tm\r
+      r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"\r
+      r.set_inactivity_timeout tm\r
+    end\r
+  end\r
+\r
+end\r
+\r
+\r
+#-----------------------------------------------------------------\r
+\r
+module EventMachine\r
+\r
+  class Error < Exception; end\r
+\r
+end\r
+\r
+#-----------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class Connection\r
+    def get_outbound_data_size\r
+      EventMachine::get_outbound_data_size @signature\r
+    end\r
+  end\r
+end\r
+\r
+#-----------------------------------------------------------------\r
+\r
+module EventMachine\r
+\r
+  # Factored out so we can substitute other implementations\r
+  # here if desired, such as the one in ActiveRBAC.\r
+  module UuidGenerator\r
+\r
+    def self.generate\r
+      if @ix and @ix >= 10000\r
+        @ix = nil\r
+        @seed = nil\r
+      end\r
+\r
+      @seed ||= `uuidgen`.chomp.gsub(/-/,"")\r
+      @ix ||= 0\r
+\r
+      "#{@seed}#{@ix += 1}"\r
+    end\r
+\r
+  end\r
+\r
+end\r
+\r
+#-----------------------------------------------------------------\r
+\r
+module EventMachine\r
+\r
+  TimerFired = 100\r
+  ConnectionData = 101\r
+  ConnectionUnbound = 102\r
+  ConnectionAccepted = 103\r
+  ConnectionCompleted = 104\r
+  LoopbreakSignalled = 105\r
+\r
+end\r
+\r
+#-----------------------------------------------------------------\r
+\r
+module EventMachine\r
+class Reactor\r
+  include Singleton\r
+\r
+  HeartbeatInterval = 2\r
+\r
+  attr_reader :current_loop_time\r
+\r
+  def initialize\r
+    initialize_for_run\r
+  end\r
+\r
+  #--\r
+  # Replaced original implementation 05Dec07, was way too slow because of the sort.\r
+  def install_oneshot_timer interval\r
+    uuid = UuidGenerator::generate\r
+    #@timers << [Time.now + interval, uuid]\r
+    #@timers.sort! {|a,b| a.first <=> b.first}\r
+    @timers.add([Time.now + interval, uuid])\r
+    uuid\r
+  end\r
+\r
+  # Called before run, this is a good place to clear out arrays\r
+  # with cruft that may be left over from a previous run.\r
+  def initialize_for_run\r
+    @running = false\r
+    @stop_scheduled = false\r
+    @selectables ||= {}; @selectables.clear\r
+    @timers = SortedSet.new # []\r
+    set_timer_quantum(0.1)\r
+    @current_loop_time = Time.now\r
+    @next_heartbeat = @current_loop_time + HeartbeatInterval\r
+  end\r
+\r
+  def add_selectable io\r
+    @selectables[io.uuid] = io\r
+  end\r
+\r
+  def get_selectable uuid\r
+    @selectables[uuid]\r
+  end\r
+\r
+  def run\r
+    raise Error.new( "already running" ) if @running\r
+    @running = true\r
+\r
+    begin\r
+      open_loopbreaker\r
+\r
+      loop {\r
+        @current_loop_time = Time.now\r
+\r
+        break if @stop_scheduled\r
+        run_timers\r
+        break if @stop_scheduled\r
+        crank_selectables\r
+        break if @stop_scheduled\r
+        run_heartbeats\r
+      }\r
+    ensure\r
+      close_loopbreaker\r
+      @selectables.each {|k, io| io.close}\r
+      @selectables.clear\r
+\r
+      @running = false\r
+    end\r
+\r
+  end\r
+\r
+  def run_timers\r
+    @timers.each {|t|\r
+      if t.first <= @current_loop_time\r
+        @timers.delete t\r
+        EventMachine::event_callback "", TimerFired, t.last\r
+      else\r
+        break\r
+      end\r
+    }\r
+    #while @timers.length > 0 and @timers.first.first <= now\r
+    #  t = @timers.shift\r
+    #  EventMachine::event_callback "", TimerFired, t.last\r
+    #end\r
+  end\r
+\r
+  def run_heartbeats\r
+    if @next_heartbeat <= @current_loop_time\r
+      @next_heartbeat = @current_loop_time + HeartbeatInterval\r
+      @selectables.each {|k,io| io.heartbeat}\r
+    end\r
+  end\r
+\r
+  def crank_selectables\r
+      #$stderr.write 'R'\r
+\r
+      readers = @selectables.values.select {|io| io.select_for_reading?}\r
+      writers = @selectables.values.select {|io| io.select_for_writing?}\r
+\r
+      s = select( readers, writers, nil, @timer_quantum)\r
+\r
+      s and s[1] and s[1].each {|w| w.eventable_write }\r
+      s and s[0] and s[0].each {|r| r.eventable_read }\r
+\r
+      @selectables.delete_if {|k,io|\r
+        if io.close_scheduled?\r
+          io.close\r
+          true\r
+        end\r
+      }\r
+  end\r
+\r
+  # #stop\r
+  def stop\r
+    raise Error.new( "not running") unless @running\r
+    @stop_scheduled = true\r
+  end\r
+\r
+  def open_loopbreaker\r
+    # Can't use an IO.pipe because they can't be set nonselectable in Windows.\r
+    # Pick a random localhost UDP port.\r
+    #@loopbreak_writer.close if @loopbreak_writer\r
+    #rd,@loopbreak_writer = IO.pipe\r
+    @loopbreak_reader = UDPSocket.new\r
+    @loopbreak_writer = UDPSocket.new\r
+    bound = false\r
+    100.times {\r
+      @loopbreak_port = rand(10000) + 40000\r
+      begin\r
+        @loopbreak_reader.bind "localhost", @loopbreak_port\r
+        bound = true\r
+        break\r
+      rescue\r
+      end\r
+    }\r
+    raise "Unable to bind Loopbreaker" unless bound\r
+    LoopbreakReader.new(@loopbreak_reader)\r
+  end\r
+\r
+  def close_loopbreaker\r
+    @loopbreak_writer.close\r
+    @loopbreak_writer = nil\r
+  end\r
+\r
+  def signal_loopbreak\r
+    #@loopbreak_writer.write '+' if @loopbreak_writer\r
+    @loopbreak_writer.send('+',0,"localhost",@loopbreak_port) if @loopbreak_writer\r
+  end\r
+\r
+  def set_timer_quantum interval_in_seconds\r
+    @timer_quantum = interval_in_seconds\r
+  end\r
+\r
+end\r
+\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+class IO\r
+  extend Forwardable\r
+  def_delegator :@my_selectable, :close_scheduled?\r
+  def_delegator :@my_selectable, :select_for_reading?\r
+  def_delegator :@my_selectable, :select_for_writing?\r
+  def_delegator :@my_selectable, :eventable_read\r
+  def_delegator :@my_selectable, :eventable_write\r
+  def_delegator :@my_selectable, :uuid\r
+  def_delegator :@my_selectable, :send_data\r
+  def_delegator :@my_selectable, :schedule_close\r
+  def_delegator :@my_selectable, :get_peername\r
+  def_delegator :@my_selectable, :send_datagram\r
+  def_delegator :@my_selectable, :get_outbound_data_size\r
+  def_delegator :@my_selectable, :set_inactivity_timeout\r
+  def_delegator :@my_selectable, :heartbeat\r
+end\r
+\r
+#--------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class Selectable\r
+\r
+    attr_reader :io, :uuid\r
+\r
+    def initialize io\r
+      @uuid = UuidGenerator.generate\r
+      @io = io\r
+      @last_activity = Reactor.instance.current_loop_time\r
+\r
+      if defined?(Fcntl::F_GETFL)\r
+        m = @io.fcntl(Fcntl::F_GETFL, 0)\r
+        @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)\r
+      else\r
+        # Windows doesn't define F_GETFL.\r
+        # It's not very reliable about setting descriptors nonblocking either.\r
+        begin\r
+          s = Socket.for_fd(@io.fileno)\r
+          s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )\r
+        rescue Errno::EINVAL, Errno::EBADF\r
+          STDERR.puts "Serious error: unable to set descriptor non-blocking"\r
+        end\r
+      end\r
+      # TODO, should set CLOEXEC on Unix?\r
+\r
+      @close_scheduled = false\r
+      @close_requested = false\r
+\r
+      se = self; @io.instance_eval { @my_selectable = se }\r
+      Reactor.instance.add_selectable @io\r
+    end\r
+\r
+    def close_scheduled?\r
+      @close_scheduled\r
+    end\r
+\r
+    def select_for_reading?\r
+      false\r
+    end\r
+\r
+    def select_for_writing?\r
+      false\r
+    end\r
+\r
+    def get_peername\r
+      nil\r
+    end\r
+\r
+    def set_inactivity_timeout tm\r
+      @inactivity_timeout = tm\r
+    end\r
+\r
+    def heartbeat\r
+    end\r
+  end\r
+\r
+end\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
+module EventMachine\r
+\r
+  class StreamObject < Selectable\r
+    def initialize io\r
+      super io\r
+      @outbound_q = []\r
+    end\r
+\r
+    # If we have to close, or a close-after-writing has been requested,\r
+    # then don't read any more data.\r
+    def select_for_reading?\r
+      true unless (@close_scheduled || @close_requested)\r
+    end\r
+\r
+    # If we have to close, don't select for writing.\r
+    # Otherwise, see if the protocol is ready to close.\r
+    # If not, see if he has data to send.\r
+    # If a close-after-writing has been requested and the outbound queue\r
+    # is empty, convert the status to close_scheduled.\r
+    def select_for_writing?\r
+      unless @close_scheduled\r
+        if @outbound_q.empty?\r
+          @close_scheduled = true if @close_requested\r
+          false\r
+        else\r
+          true\r
+        end\r
+      end\r
+    end\r
+\r
+    # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.\r
+    # If we have it, then we can read multiple times safely to improve\r
+    # performance.\r
+    # The last-activity clock ASSUMES that we only come here when we\r
+    # have selected readable.\r
+    # TODO, coalesce multiple reads into a single event.\r
+    # TODO, do the function check somewhere else and cache it.\r
+    def eventable_read\r
+      @last_activity = Reactor.instance.current_loop_time\r
+      begin\r
+        if io.respond_to?(:read_nonblock)\r
+          10.times {\r
+            data = io.read_nonblock(4096)\r
+            EventMachine::event_callback uuid, ConnectionData, data\r
+          }\r
+        else\r
+          data = io.sysread(4096)\r
+          EventMachine::event_callback uuid, ConnectionData, data\r
+        end\r
+      rescue Errno::EAGAIN, Errno::EWOULDBLOCK\r
+        # no-op\r
+      rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError\r
+        @close_scheduled = true\r
+        EventMachine::event_callback uuid, ConnectionUnbound, nil\r
+      end\r
+\r
+    end\r
+\r
+    # Provisional implementation. Will be re-implemented in subclasses.\r
+    # TODO: Complete this implementation. As it stands, this only writes\r
+    # a single packet per cycle. Highly inefficient, but required unless\r
+    # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4\r
+    # built from sources from May 25, 2006 or newer).\r
+    # We need to improve the loop so it writes multiple times, however\r
+    # not more than a certain number of bytes per cycle, otherwise\r
+    # one busy connection could hog output buffers and slow down other\r
+    # connections. Also we should coalesce small writes.\r
+    # URGENT TODO: Coalesce small writes. They are a performance killer.\r
+    # The last-activity recorder ASSUMES we'll only come here if we've\r
+    # selected writable.\r
+    def eventable_write\r
+      # coalesce the outbound array here, perhaps\r
+      @last_activity = Reactor.instance.current_loop_time\r
+      while data = @outbound_q.shift do\r
+        begin\r
+          data = data.to_s\r
+          w = if io.respond_to?(:write_nonblock)\r
+            io.write_nonblock data\r
+          else\r
+            io.syswrite data\r
+          end\r
+\r
+          if w < data.length\r
+            @outbound_q.unshift data[w..-1]\r
+            break\r
+          end\r
+        rescue Errno::EAGAIN\r
+          @outbound_q.unshift data\r
+        rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED\r
+          @close_scheduled = true\r
+          @outbound_q.clear\r
+        end\r
+      end\r
+\r
+    end\r
+\r
+    # #send_data\r
+    def send_data data\r
+      # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?\r
+      unless @close_scheduled or @close_requested or !data or data.length <= 0\r
+        @outbound_q << data.to_s\r
+      end\r
+    end\r
+\r
+    # #schedule_close\r
+    # The application wants to close the connection.\r
+    def schedule_close after_writing\r
+      if after_writing\r
+        @close_requested = true\r
+      else\r
+        @close_scheduled = true\r
+      end\r
+    end\r
+\r
+    # #get_peername\r
+    # This is defined in the normal way on connected stream objects.\r
+    # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.\r
+    # We could also use a convenience method that did the unpacking automatically.\r
+    def get_peername\r
+      io.getpeername\r
+    end\r
+\r
+    # #get_outbound_data_size\r
+    def get_outbound_data_size\r
+      @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}\r
+    end\r
+\r
+    def heartbeat\r
+      if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time\r
+        schedule_close true\r
+      end\r
+    end\r
+  end\r
+\r
+\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
+\r
+module EventMachine\r
+  class EvmaTCPClient < StreamObject\r
+\r
+    def self.connect bind_addr, bind_port, host, port\r
+      sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )\r
+      sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr ))  if bind_addr\r
+\r
+      begin\r
+        # TODO, this assumes a current Ruby snapshot.\r
+        # We need to degrade to a nonblocking connect otherwise.\r
+        sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))\r
+      rescue Errno::EINPROGRESS\r
+      end\r
+      EvmaTCPClient.new sd\r
+    end\r
+\r
+\r
+    def initialize io\r
+      super\r
+      @pending = true\r
+    end\r
+\r
+\r
+    def select_for_writing?\r
+      @pending ? true : super\r
+    end\r
+\r
+    def select_for_reading?\r
+      @pending ? false : super\r
+    end\r
+\r
+    def eventable_write\r
+      if @pending\r
+        @pending = false\r
+        if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first\r
+          EventMachine::event_callback uuid, ConnectionCompleted, ""\r
+        end\r
+      else\r
+        super\r
+      end\r
+    end\r
+\r
+\r
+\r
+  end\r
+end\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
+\r
+module EventMachine\r
+  class EvmaKeyboard < StreamObject\r
+\r
+    def self.open\r
+      EvmaKeyboard.new STDIN\r
+    end\r
+\r
+\r
+    def initialize io\r
+      super\r
+    end\r
+\r
+\r
+    def select_for_writing?\r
+      false\r
+    end\r
+\r
+    def select_for_reading?\r
+      true\r
+    end\r
+\r
+\r
+  end\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
+\r
+module EventMachine\r
+  class EvmaUNIXClient < StreamObject\r
+\r
+    def self.connect chain\r
+      sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )\r
+      begin\r
+        # TODO, this assumes a current Ruby snapshot.\r
+        # We need to degrade to a nonblocking connect otherwise.\r
+        sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))\r
+      rescue Errno::EINPROGRESS\r
+      end\r
+      EvmaUNIXClient.new sd\r
+    end\r
+\r
+\r
+    def initialize io\r
+      super\r
+      @pending = true\r
+    end\r
+\r
+\r
+    def select_for_writing?\r
+      @pending ? true : super\r
+    end\r
+\r
+    def select_for_reading?\r
+      @pending ? false : super\r
+    end\r
+\r
+    def eventable_write\r
+      if @pending\r
+        @pending = false\r
+        if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first\r
+          EventMachine::event_callback uuid, ConnectionCompleted, ""\r
+        end\r
+      else\r
+        super\r
+      end\r
+    end\r
+\r
+\r
+\r
+  end\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class EvmaTCPServer < Selectable\r
+\r
+    # TODO, refactor and unify with EvmaUNIXServer.\r
+\r
+    class << self\r
+      # Versions of ruby 1.8.4 later than May 26 2006 will work properly\r
+      # with an object of type TCPServer. Prior versions won't so we\r
+      # play it safe and just build a socket.\r
+      #\r
+      def start_server host, port\r
+        sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )\r
+        sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )\r
+        sd.bind( Socket.pack_sockaddr_in( port, host ))\r
+        sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.\r
+        EvmaTCPServer.new sd\r
+      end\r
+    end\r
+\r
+    def initialize io\r
+      super io\r
+    end\r
+\r
+\r
+    def select_for_reading?\r
+      true\r
+    end\r
+\r
+    #--\r
+    # accept_nonblock returns an array consisting of the accepted\r
+    # socket and a sockaddr_in which names the peer.\r
+    # Don't accept more than 10 at a time.\r
+    def eventable_read\r
+      begin\r
+        10.times {\r
+          descriptor,peername = io.accept_nonblock\r
+          sd = StreamObject.new descriptor\r
+          EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid\r
+        }\r
+      rescue Errno::EWOULDBLOCK, Errno::EAGAIN\r
+      end\r
+    end\r
+\r
+    #--\r
+    #\r
+    def schedule_close\r
+      @close_scheduled = true\r
+    end\r
+\r
+  end\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class EvmaUNIXServer < Selectable\r
+\r
+    # TODO, refactor and unify with EvmaTCPServer.\r
+\r
+    class << self\r
+      # Versions of ruby 1.8.4 later than May 26 2006 will work properly\r
+      # with an object of type TCPServer. Prior versions won't so we\r
+      # play it safe and just build a socket.\r
+      #\r
+      def start_server chain\r
+        sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )\r
+        sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )\r
+        sd.bind( Socket.pack_sockaddr_un( chain ))\r
+        sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.\r
+        EvmaUNIXServer.new sd\r
+      end\r
+    end\r
+\r
+    def initialize io\r
+      super io\r
+    end\r
+\r
+\r
+    def select_for_reading?\r
+      true\r
+    end\r
+\r
+    #--\r
+    # accept_nonblock returns an array consisting of the accepted\r
+    # socket and a sockaddr_in which names the peer.\r
+    # Don't accept more than 10 at a time.\r
+    def eventable_read\r
+      begin\r
+        10.times {\r
+          descriptor,peername = io.accept_nonblock\r
+          sd = StreamObject.new descriptor\r
+          EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid\r
+        }\r
+      rescue Errno::EWOULDBLOCK, Errno::EAGAIN\r
+      end\r
+    end\r
+\r
+    #--\r
+    #\r
+    def schedule_close\r
+      @close_scheduled = true\r
+    end\r
+\r
+  end\r
+end\r
+\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class LoopbreakReader < Selectable\r
+\r
+    def select_for_reading?\r
+      true\r
+    end\r
+\r
+    def eventable_read\r
+          io.sysread(128)\r
+          EventMachine::event_callback "", LoopbreakSignalled, ""\r
+    end\r
+\r
+  end\r
+end\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
+module EventMachine\r
+\r
+  class DatagramObject < Selectable\r
+    def initialize io\r
+      super io\r
+      @outbound_q = []\r
+    end\r
+\r
+    # #send_datagram\r
+    def send_datagram data, target\r
+      # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?\r
+      unless @close_scheduled or @close_requested\r
+        @outbound_q << [data.to_s, target]\r
+      end\r
+    end\r
+\r
+    # #select_for_writing?\r
+    def select_for_writing?\r
+      unless @close_scheduled\r
+        if @outbound_q.empty?\r
+          @close_scheduled = true if @close_requested\r
+          false\r
+        else\r
+          true\r
+        end\r
+      end\r
+    end\r
+\r
+    # #select_for_reading?\r
+    def select_for_reading?\r
+      true\r
+    end\r
+\r
+    # #get_outbound_data_size\r
+    def get_outbound_data_size\r
+      @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}\r
+    end\r
+\r
+\r
+  end\r
+\r
+\r
+end\r
+\r
+\r
+#--------------------------------------------------------------\r
+\r
+module EventMachine\r
+  class EvmaUDPSocket < DatagramObject\r
+\r
+    class << self\r
+      def create host, port\r
+        sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )\r
+        sd.bind Socket::pack_sockaddr_in( port, host )\r
+        EvmaUDPSocket.new sd\r
+      end\r
+    end\r
+\r
+    # #eventable_write\r
+    # This really belongs in DatagramObject, but there is some UDP-specific stuff.\r
+    def eventable_write\r
+      40.times {\r
+        break if @outbound_q.empty?\r
+        begin\r
+          data,target = @outbound_q.first\r
+\r
+          # This damn better be nonblocking.\r
+          io.send data.to_s, 0, target\r
+\r
+          @outbound_q.shift\r
+        rescue Errno::EAGAIN\r
+          # It's not been observed in testing that we ever get here.\r
+          # True to the definition, packets will be accepted and quietly dropped\r
+          # if the system is under pressure.\r
+          break\r
+        rescue EOFError, Errno::ECONNRESET\r
+          @close_scheduled = true\r
+          @outbound_q.clear\r
+        end\r
+      }\r
+    end\r
+\r
+    # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.\r
+    # If we have it, then we can read multiple times safely to improve\r
+    # performance.\r
+    def eventable_read\r
+      begin\r
+        if io.respond_to?(:recvfrom_nonblock)\r
+          40.times {\r
+            data,@return_address = io.recvfrom_nonblock(16384)\r
+            EventMachine::event_callback uuid, ConnectionData, data\r
+            @return_address = nil\r
+          }\r
+        else\r
+          raise "unimplemented datagram-read operation on this Ruby"\r
+        end\r
+      rescue Errno::EAGAIN\r
+        # no-op\r
+      rescue Errno::ECONNRESET, EOFError\r
+        @close_scheduled = true\r
+        EventMachine::event_callback uuid, ConnectionUnbound, nil\r
+      end\r
+\r
+    end\r
+\r
+\r
+    def send_data data\r
+      send_datagram data, @return_address\r
+    end\r
+\r
+  end\r
+end\r
+\r
+#--------------------------------------------------------------\r
+\r
+\r
-#
-# setup.rb
-#
-# Copyright (c) 2000-2005 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the terms of
-# the GNU LGPL, Lesser General Public License version 2.1.
-#
-
-unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
-  module Enumerable
-    alias map collect
-  end
-end
-
-unless File.respond_to?(:read)   # Ruby 1.6
-  def File.read(fname)
-    open(fname) {|f|
-      return f.read
-    }
-  end
-end
-
-unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
-  module Errno
-    class ENOTEMPTY
-      # We do not raise this exception, implementation is not needed.
-    end
-  end
-end
-
-def File.binread(fname)
-  open(fname, 'rb') {|f|
-    return f.read
-  }
-end
-
-# for corrupted Windows' stat(2)
-def File.dir?(path)
-  File.directory?((path[-1,1] == '/') ? path : path + '/')
-end
-
-
-class ConfigTable
-
-  include Enumerable
-
-  def initialize(rbconfig)
-    @rbconfig = rbconfig
-    @items = []
-    @table = {}
-    # options
-    @install_prefix = nil
-    @config_opt = nil
-    @verbose = true
-    @no_harm = false
-  end
-
-  attr_accessor :install_prefix
-  attr_accessor :config_opt
-
-  attr_writer :verbose
-
-  def verbose?
-    @verbose
-  end
-
-  attr_writer :no_harm
-
-  def no_harm?
-    @no_harm
-  end
-
-  def [](key)
-    lookup(key).resolve(self)
-  end
-
-  def []=(key, val)
-    lookup(key).set val
-  end
-
-  def names
-    @items.map {|i| i.name }
-  end
-
-  def each(&block)
-    @items.each(&block)
-  end
-
-  def key?(name)
-    @table.key?(name)
-  end
-
-  def lookup(name)
-    @table[name] or setup_rb_error "no such config item: #{name}"
-  end
-
-  def add(item)
-    @items.push item
-    @table[item.name] = item
-  end
-
-  def remove(name)
-    item = lookup(name)
-    @items.delete_if {|i| i.name == name }
-    @table.delete_if {|name, i| i.name == name }
-    item
-  end
-
-  def load_script(path, inst = nil)
-    if File.file?(path)
-      MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
-    end
-  end
-
-  def savefile
-    '.config'
-  end
-
-  def load_savefile
-    begin
-      File.foreach(savefile()) do |line|
-        k, v = *line.split(/=/, 2)
-        self[k] = v.strip
-      end
-    rescue Errno::ENOENT
-      setup_rb_error $!.message + "\n#{File.basename($0)} config first"
-    end
-  end
-
-  def save
-    @items.each {|i| i.value }
-    File.open(savefile(), 'w') {|f|
-      @items.each do |i|
-        f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
-      end
-    }
-  end
-
-  def load_standard_entries
-    standard_entries(@rbconfig).each do |ent|
-      add ent
-    end
-  end
-
-  def standard_entries(rbconfig)
-    c = rbconfig
-
-    rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
-
-    major = c['MAJOR'].to_i
-    minor = c['MINOR'].to_i
-    teeny = c['TEENY'].to_i
-    version = "#{major}.#{minor}"
-
-    # ruby ver. >= 1.4.4?
-    newpath_p = ((major >= 2) or
-                 ((major == 1) and
-                  ((minor >= 5) or
-                   ((minor == 4) and (teeny >= 4)))))
-
-    if c['rubylibdir']
-      # V > 1.6.3
-      libruby         = "#{c['prefix']}/lib/ruby"
-      librubyver      = c['rubylibdir']
-      librubyverarch  = c['archdir']
-      siteruby        = c['sitedir']
-      siterubyver     = c['sitelibdir']
-      siterubyverarch = c['sitearchdir']
-    elsif newpath_p
-      # 1.4.4 <= V <= 1.6.3
-      libruby         = "#{c['prefix']}/lib/ruby"
-      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
-      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
-      siteruby        = c['sitedir']
-      siterubyver     = "$siteruby/#{version}"
-      siterubyverarch = "$siterubyver/#{c['arch']}"
-    else
-      # V < 1.4.4
-      libruby         = "#{c['prefix']}/lib/ruby"
-      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
-      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
-      siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
-      siterubyver     = siteruby
-      siterubyverarch = "$siterubyver/#{c['arch']}"
-    end
-    parameterize = lambda {|path|
-      path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
-    }
-
-    if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
-      makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
-    else
-      makeprog = 'make'
-    end
-
-    [
-      ExecItem.new('installdirs', 'std/site/home',
-                   'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
-          {|val, table|
-            case val
-            when 'std'
-              table['rbdir'] = '$librubyver'
-              table['sodir'] = '$librubyverarch'
-            when 'site'
-              table['rbdir'] = '$siterubyver'
-              table['sodir'] = '$siterubyverarch'
-            when 'home'
-              setup_rb_error '$HOME was not set' unless ENV['HOME']
-              table['prefix'] = ENV['HOME']
-              table['rbdir'] = '$libdir/ruby'
-              table['sodir'] = '$libdir/ruby'
-            end
-          },
-      PathItem.new('prefix', 'path', c['prefix'],
-                   'path prefix of target environment'),
-      PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
-                   'the directory for commands'),
-      PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
-                   'the directory for libraries'),
-      PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
-                   'the directory for shared data'),
-      PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
-                   'the directory for man pages'),
-      PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
-                   'the directory for system configuration files'),
-      PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
-                   'the directory for local state data'),
-      PathItem.new('libruby', 'path', libruby,
-                   'the directory for ruby libraries'),
-      PathItem.new('librubyver', 'path', librubyver,
-                   'the directory for standard ruby libraries'),
-      PathItem.new('librubyverarch', 'path', librubyverarch,
-                   'the directory for standard ruby extensions'),
-      PathItem.new('siteruby', 'path', siteruby,
-          'the directory for version-independent aux ruby libraries'),
-      PathItem.new('siterubyver', 'path', siterubyver,
-                   'the directory for aux ruby libraries'),
-      PathItem.new('siterubyverarch', 'path', siterubyverarch,
-                   'the directory for aux ruby binaries'),
-      PathItem.new('rbdir', 'path', '$siterubyver',
-                   'the directory for ruby scripts'),
-      PathItem.new('sodir', 'path', '$siterubyverarch',
-                   'the directory for ruby extentions'),
-      PathItem.new('rubypath', 'path', rubypath,
-                   'the path to set to #! line'),
-      ProgramItem.new('rubyprog', 'name', rubypath,
-                      'the ruby program using for installation'),
-      ProgramItem.new('makeprog', 'name', makeprog,
-                      'the make program to compile ruby extentions'),
-      SelectItem.new('shebang', 'all/ruby/never', 'ruby',
-                     'shebang line (#!) editing mode'),
-      BoolItem.new('without-ext', 'yes/no', 'no',
-                   'does not compile/install ruby extentions')
-    ]
-  end
-  private :standard_entries
-
-  def load_multipackage_entries
-    multipackage_entries().each do |ent|
-      add ent
-    end
-  end
-
-  def multipackage_entries
-    [
-      PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
-                               'package names that you want to install'),
-      PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
-                               'package names that you do not want to install')
-    ]
-  end
-  private :multipackage_entries
-
-  ALIASES = {
-    'std-ruby'         => 'librubyver',
-    'stdruby'          => 'librubyver',
-    'rubylibdir'       => 'librubyver',
-    'archdir'          => 'librubyverarch',
-    'site-ruby-common' => 'siteruby',     # For backward compatibility
-    'site-ruby'        => 'siterubyver',  # For backward compatibility
-    'bin-dir'          => 'bindir',
-    'bin-dir'          => 'bindir',
-    'rb-dir'           => 'rbdir',
-    'so-dir'           => 'sodir',
-    'data-dir'         => 'datadir',
-    'ruby-path'        => 'rubypath',
-    'ruby-prog'        => 'rubyprog',
-    'ruby'             => 'rubyprog',
-    'make-prog'        => 'makeprog',
-    'make'             => 'makeprog'
-  }
-
-  def fixup
-    ALIASES.each do |ali, name|
-      @table[ali] = @table[name]
-    end
-    @items.freeze
-    @table.freeze
-    @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
-  end
-
-  def parse_opt(opt)
-    m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
-    m.to_a[1,2]
-  end
-
-  def dllext
-    @rbconfig['DLEXT']
-  end
-
-  def value_config?(name)
-    lookup(name).value?
-  end
-
-  class Item
-    def initialize(name, template, default, desc)
-      @name = name.freeze
-      @template = template
-      @value = default
-      @default = default
-      @description = desc
-    end
-
-    attr_reader :name
-    attr_reader :description
-
-    attr_accessor :default
-    alias help_default default
-
-    def help_opt
-      "--#{@name}=#{@template}"
-    end
-
-    def value?
-      true
-    end
-
-    def value
-      @value
-    end
-
-    def resolve(table)
-      @value.gsub(%r<\$([^/]+)>) { table[$1] }
-    end
-
-    def set(val)
-      @value = check(val)
-    end
-
-    private
-
-    def check(val)
-      setup_rb_error "config: --#{name} requires argument" unless val
-      val
-    end
-  end
-
-  class BoolItem < Item
-    def config_type
-      'bool'
-    end
-
-    def help_opt
-      "--#{@name}"
-    end
-
-    private
-
-    def check(val)
-      return 'yes' unless val
-      case val
-      when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
-      when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'
-      else
-        setup_rb_error "config: --#{@name} accepts only yes/no for argument"
-      end
-    end
-  end
-
-  class PathItem < Item
-    def config_type
-      'path'
-    end
-
-    private
-
-    def check(path)
-      setup_rb_error "config: --#{@name} requires argument"  unless path
-      path[0,1] == '$' ? path : File.expand_path(path)
-    end
-  end
-
-  class ProgramItem < Item
-    def config_type
-      'program'
-    end
-  end
-
-  class SelectItem < Item
-    def initialize(name, selection, default, desc)
-      super
-      @ok = selection.split('/')
-    end
-
-    def config_type
-      'select'
-    end
-
-    private
-
-    def check(val)
-      unless @ok.include?(val.strip)
-        setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
-      end
-      val.strip
-    end
-  end
-
-  class ExecItem < Item
-    def initialize(name, selection, desc, &block)
-      super name, selection, nil, desc
-      @ok = selection.split('/')
-      @action = block
-    end
-
-    def config_type
-      'exec'
-    end
-
-    def value?
-      false
-    end
-
-    def resolve(table)
-      setup_rb_error "$#{name()} wrongly used as option value"
-    end
-
-    undef set
-
-    def evaluate(val, table)
-      v = val.strip.downcase
-      unless @ok.include?(v)
-        setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
-      end
-      @action.call v, table
-    end
-  end
-
-  class PackageSelectionItem < Item
-    def initialize(name, template, default, help_default, desc)
-      super name, template, default, desc
-      @help_default = help_default
-    end
-
-    attr_reader :help_default
-
-    def config_type
-      'package'
-    end
-
-    private
-
-    def check(val)
-      unless File.dir?("packages/#{val}")
-        setup_rb_error "config: no such package: #{val}"
-      end
-      val
-    end
-  end
-
-  class MetaConfigEnvironment
-    def initialize(config, installer)
-      @config = config
-      @installer = installer
-    end
-
-    def config_names
-      @config.names
-    end
-
-    def config?(name)
-      @config.key?(name)
-    end
-
-    def bool_config?(name)
-      @config.lookup(name).config_type == 'bool'
-    end
-
-    def path_config?(name)
-      @config.lookup(name).config_type == 'path'
-    end
-
-    def value_config?(name)
-      @config.lookup(name).config_type != 'exec'
-    end
-
-    def add_config(item)
-      @config.add item
-    end
-
-    def add_bool_config(name, default, desc)
-      @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
-    end
-
-    def add_path_config(name, default, desc)
-      @config.add PathItem.new(name, 'path', default, desc)
-    end
-
-    def set_config_default(name, default)
-      @config.lookup(name).default = default
-    end
-
-    def remove_config(name)
-      @config.remove(name)
-    end
-
-    # For only multipackage
-    def packages
-      raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
-      @installer.packages
-    end
-
-    # For only multipackage
-    def declare_packages(list)
-      raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
-      @installer.packages = list
-    end
-  end
-
-end   # class ConfigTable
-
-
-# This module requires: #verbose?, #no_harm?
-module FileOperations
-
-  def mkdir_p(dirname, prefix = nil)
-    dirname = prefix + File.expand_path(dirname) if prefix
-    $stderr.puts "mkdir -p #{dirname}" if verbose?
-    return if no_harm?
-
-    # Does not check '/', it's too abnormal.
-    dirs = File.expand_path(dirname).split(%r<(?=/)>)
-    if /\A[a-z]:\z/i =~ dirs[0]
-      disk = dirs.shift
-      dirs[0] = disk + dirs[0]
-    end
-    dirs.each_index do |idx|
-      path = dirs[0..idx].join('')
-      Dir.mkdir path unless File.dir?(path)
-    end
-  end
-
-  def rm_f(path)
-    $stderr.puts "rm -f #{path}" if verbose?
-    return if no_harm?
-    force_remove_file path
-  end
-
-  def rm_rf(path)
-    $stderr.puts "rm -rf #{path}" if verbose?
-    return if no_harm?
-    remove_tree path
-  end
-
-  def remove_tree(path)
-    if File.symlink?(path)
-      remove_file path
-    elsif File.dir?(path)
-      remove_tree0 path
-    else
-      force_remove_file path
-    end
-  end
-
-  def remove_tree0(path)
-    Dir.foreach(path) do |ent|
-      next if ent == '.'
-      next if ent == '..'
-      entpath = "#{path}/#{ent}"
-      if File.symlink?(entpath)
-        remove_file entpath
-      elsif File.dir?(entpath)
-        remove_tree0 entpath
-      else
-        force_remove_file entpath
-      end
-    end
-    begin
-      Dir.rmdir path
-    rescue Errno::ENOTEMPTY
-      # directory may not be empty
-    end
-  end
-
-  def move_file(src, dest)
-    force_remove_file dest
-    begin
-      File.rename src, dest
-    rescue
-      File.open(dest, 'wb') {|f|
-        f.write File.binread(src)
-      }
-      File.chmod File.stat(src).mode, dest
-      File.unlink src
-    end
-  end
-
-  def force_remove_file(path)
-    begin
-      remove_file path
-    rescue
-    end
-  end
-
-  def remove_file(path)
-    File.chmod 0777, path
-    File.unlink path
-  end
-
-  def install(from, dest, mode, prefix = nil)
-    $stderr.puts "install #{from} #{dest}" if verbose?
-    return if no_harm?
-
-    realdest = prefix ? prefix + File.expand_path(dest) : dest
-    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
-    str = File.binread(from)
-    if diff?(str, realdest)
-      verbose_off {
-        rm_f realdest if File.exist?(realdest)
-      }
-      File.open(realdest, 'wb') {|f|
-        f.write str
-      }
-      File.chmod mode, realdest
-
-      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
-        if prefix
-          f.puts realdest.sub(prefix, '')
-        else
-          f.puts realdest
-        end
-      }
-    end
-  end
-
-  def diff?(new_content, path)
-    return true unless File.exist?(path)
-    new_content != File.binread(path)
-  end
-
-  def command(*args)
-    $stderr.puts args.join(' ') if verbose?
-    system(*args) or raise RuntimeError,
-        "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
-  end
-
-  def ruby(*args)
-    command config('rubyprog'), *args
-  end
-  
-  def make(task = nil)
-    command(*[config('makeprog'), task].compact)
-  end
-
-  def extdir?(dir)
-    File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
-  end
-
-  def files_of(dir)
-    Dir.open(dir) {|d|
-      return d.select {|ent| File.file?("#{dir}/#{ent}") }
-    }
-  end
-
-  DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
-
-  def directories_of(dir)
-    Dir.open(dir) {|d|
-      return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
-    }
-  end
-
-end
-
-
-# This module requires: #srcdir_root, #objdir_root, #relpath
-module HookScriptAPI
-
-  def get_config(key)
-    @config[key]
-  end
-
-  alias config get_config
-
-  # obsolete: use metaconfig to change configuration
-  def set_config(key, val)
-    @config[key] = val
-  end
-
-  #
-  # srcdir/objdir (works only in the package directory)
-  #
-
-  def curr_srcdir
-    "#{srcdir_root()}/#{relpath()}"
-  end
-
-  def curr_objdir
-    "#{objdir_root()}/#{relpath()}"
-  end
-
-  def srcfile(path)
-    "#{curr_srcdir()}/#{path}"
-  end
-
-  def srcexist?(path)
-    File.exist?(srcfile(path))
-  end
-
-  def srcdirectory?(path)
-    File.dir?(srcfile(path))
-  end
-  
-  def srcfile?(path)
-    File.file?(srcfile(path))
-  end
-
-  def srcentries(path = '.')
-    Dir.open("#{curr_srcdir()}/#{path}") {|d|
-      return d.to_a - %w(. ..)
-    }
-  end
-
-  def srcfiles(path = '.')
-    srcentries(path).select {|fname|
-      File.file?(File.join(curr_srcdir(), path, fname))
-    }
-  end
-
-  def srcdirectories(path = '.')
-    srcentries(path).select {|fname|
-      File.dir?(File.join(curr_srcdir(), path, fname))
-    }
-  end
-
-end
-
-
-class ToplevelInstaller
-
-  Version   = '3.4.1'
-  Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
-
-  TASKS = [
-    [ 'all',      'do config, setup, then install' ],
-    [ 'config',   'saves your configurations' ],
-    [ 'show',     'shows current configuration' ],
-    [ 'setup',    'compiles ruby extentions and others' ],
-    [ 'install',  'installs files' ],
-    [ 'test',     'run all tests in test/' ],
-    [ 'clean',    "does `make clean' for each extention" ],
-    [ 'distclean',"does `make distclean' for each extention" ]
-  ]
-
-  def ToplevelInstaller.invoke
-    config = ConfigTable.new(load_rbconfig())
-    config.load_standard_entries
-    config.load_multipackage_entries if multipackage?
-    config.fixup
-    klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
-    klass.new(File.dirname($0), config).invoke
-  end
-
-  def ToplevelInstaller.multipackage?
-    File.dir?(File.dirname($0) + '/packages')
-  end
-
-  def ToplevelInstaller.load_rbconfig
-    if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
-      ARGV.delete(arg)
-      load File.expand_path(arg.split(/=/, 2)[1])
-      $".push 'rbconfig.rb'
-    else
-      require 'rbconfig'
-    end
-    ::Config::CONFIG
-  end
-
-  def initialize(ardir_root, config)
-    @ardir = File.expand_path(ardir_root)
-    @config = config
-    # cache
-    @valid_task_re = nil
-  end
-
-  def config(key)
-    @config[key]
-  end
-
-  def inspect
-    "#<#{self.class} #{__id__()}>"
-  end
-
-  def invoke
-    run_metaconfigs
-    case task = parsearg_global()
-    when nil, 'all'
-      parsearg_config
-      init_installers
-      exec_config
-      exec_setup
-      exec_install
-    else
-      case task
-      when 'config', 'test'
-        ;
-      when 'clean', 'distclean'
-        @config.load_savefile if File.exist?(@config.savefile)
-      else
-        @config.load_savefile
-      end
-      __send__ "parsearg_#{task}"
-      init_installers
-      __send__ "exec_#{task}"
-    end
-  end
-  
-  def run_metaconfigs
-    @config.load_script "#{@ardir}/metaconfig"
-  end
-
-  def init_installers
-    @installer = Installer.new(@config, @ardir, File.expand_path('.'))
-  end
-
-  #
-  # Hook Script API bases
-  #
-
-  def srcdir_root
-    @ardir
-  end
-
-  def objdir_root
-    '.'
-  end
-
-  def relpath
-    '.'
-  end
-
-  #
-  # Option Parsing
-  #
-
-  def parsearg_global
-    while arg = ARGV.shift
-      case arg
-      when /\A\w+\z/
-        setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
-        return arg
-      when '-q', '--quiet'
-        @config.verbose = false
-      when '--verbose'
-        @config.verbose = true
-      when '--help'
-        print_usage $stdout
-        exit 0
-      when '--version'
-        puts "#{File.basename($0)} version #{Version}"
-        exit 0
-      when '--copyright'
-        puts Copyright
-        exit 0
-      else
-        setup_rb_error "unknown global option '#{arg}'"
-      end
-    end
-    nil
-  end
-
-  def valid_task?(t)
-    valid_task_re() =~ t
-  end
-
-  def valid_task_re
-    @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
-  end
-
-  def parsearg_no_options
-    unless ARGV.empty?
-      task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
-      setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
-    end
-  end
-
-  alias parsearg_show       parsearg_no_options
-  alias parsearg_setup      parsearg_no_options
-  alias parsearg_test       parsearg_no_options
-  alias parsearg_clean      parsearg_no_options
-  alias parsearg_distclean  parsearg_no_options
-
-  def parsearg_config
-    evalopt = []
-    set = []
-    @config.config_opt = []
-    while i = ARGV.shift
-      if /\A--?\z/ =~ i
-        @config.config_opt = ARGV.dup
-        break
-      end
-      name, value = *@config.parse_opt(i)
-      if @config.value_config?(name)
-        @config[name] = value
-      else
-        evalopt.push [name, value]
-      end
-      set.push name
-    end
-    evalopt.each do |name, value|
-      @config.lookup(name).evaluate value, @config
-    end
-    # Check if configuration is valid
-    set.each do |n|
-      @config[n] if @config.value_config?(n)
-    end
-  end
-
-  def parsearg_install
-    @config.no_harm = false
-    @config.install_prefix = ''
-    while a = ARGV.shift
-      case a
-      when '--no-harm'
-        @config.no_harm = true
-      when /\A--prefix=/
-        path = a.split(/=/, 2)[1]
-        path = File.expand_path(path) unless path[0,1] == '/'
-        @config.install_prefix = path
-      else
-        setup_rb_error "install: unknown option #{a}"
-      end
-    end
-  end
-
-  def print_usage(out)
-    out.puts 'Typical Installation Procedure:'
-    out.puts "  $ ruby #{File.basename $0} config"
-    out.puts "  $ ruby #{File.basename $0} setup"
-    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
-    out.puts
-    out.puts 'Detailed Usage:'
-    out.puts "  ruby #{File.basename $0} <global option>"
-    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
-
-    fmt = "  %-24s %s\n"
-    out.puts
-    out.puts 'Global options:'
-    out.printf fmt, '-q,--quiet',   'suppress message outputs'
-    out.printf fmt, '   --verbose', 'output messages verbosely'
-    out.printf fmt, '   --help',    'print this message'
-    out.printf fmt, '   --version', 'print version and quit'
-    out.printf fmt, '   --copyright',  'print copyright and quit'
-    out.puts
-    out.puts 'Tasks:'
-    TASKS.each do |name, desc|
-      out.printf fmt, name, desc
-    end
-
-    fmt = "  %-24s %s [%s]\n"
-    out.puts
-    out.puts 'Options for CONFIG or ALL:'
-    @config.each do |item|
-      out.printf fmt, item.help_opt, item.description, item.help_default
-    end
-    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
-    out.puts
-    out.puts 'Options for INSTALL:'
-    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
-    out.printf fmt, '--prefix=path',  'install path prefix', ''
-    out.puts
-  end
-
-  #
-  # Task Handlers
-  #
-
-  def exec_config
-    @installer.exec_config
-    @config.save   # must be final
-  end
-
-  def exec_setup
-    @installer.exec_setup
-  end
-
-  def exec_install
-    @installer.exec_install
-  end
-
-  def exec_test
-    @installer.exec_test
-  end
-
-  def exec_show
-    @config.each do |i|
-      printf "%-20s %s\n", i.name, i.value if i.value?
-    end
-  end
-
-  def exec_clean
-    @installer.exec_clean
-  end
-
-  def exec_distclean
-    @installer.exec_distclean
-  end
-
-end   # class ToplevelInstaller
-
-
-class ToplevelInstallerMulti < ToplevelInstaller
-
-  include FileOperations
-
-  def initialize(ardir_root, config)
-    super
-    @packages = directories_of("#{@ardir}/packages")
-    raise 'no package exists' if @packages.empty?
-    @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
-  end
-
-  def run_metaconfigs
-    @config.load_script "#{@ardir}/metaconfig", self
-    @packages.each do |name|
-      @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
-    end
-  end
-
-  attr_reader :packages
-
-  def packages=(list)
-    raise 'package list is empty' if list.empty?
-    list.each do |name|
-      raise "directory packages/#{name} does not exist"\
-              unless File.dir?("#{@ardir}/packages/#{name}")
-    end
-    @packages = list
-  end
-
-  def init_installers
-    @installers = {}
-    @packages.each do |pack|
-      @installers[pack] = Installer.new(@config,
-                                       "#{@ardir}/packages/#{pack}",
-                                       "packages/#{pack}")
-    end
-    with    = extract_selection(config('with'))
-    without = extract_selection(config('without'))
-    @selected = @installers.keys.select {|name|
-                  (with.empty? or with.include?(name)) \
-                      and not without.include?(name)
-                }
-  end
-
-  def extract_selection(list)
-    a = list.split(/,/)
-    a.each do |name|
-      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
-    end
-    a
-  end
-
-  def print_usage(f)
-    super
-    f.puts 'Inluded packages:'
-    f.puts '  ' + @packages.sort.join(' ')
-    f.puts
-  end
-
-  #
-  # Task Handlers
-  #
-
-  def exec_config
-    run_hook 'pre-config'
-    each_selected_installers {|inst| inst.exec_config }
-    run_hook 'post-config'
-    @config.save   # must be final
-  end
-
-  def exec_setup
-    run_hook 'pre-setup'
-    each_selected_installers {|inst| inst.exec_setup }
-    run_hook 'post-setup'
-  end
-
-  def exec_install
-    run_hook 'pre-install'
-    each_selected_installers {|inst| inst.exec_install }
-    run_hook 'post-install'
-  end
-
-  def exec_test
-    run_hook 'pre-test'
-    each_selected_installers {|inst| inst.exec_test }
-    run_hook 'post-test'
-  end
-
-  def exec_clean
-    rm_f @config.savefile
-    run_hook 'pre-clean'
-    each_selected_installers {|inst| inst.exec_clean }
-    run_hook 'post-clean'
-  end
-
-  def exec_distclean
-    rm_f @config.savefile
-    run_hook 'pre-distclean'
-    each_selected_installers {|inst| inst.exec_distclean }
-    run_hook 'post-distclean'
-  end
-
-  #
-  # lib
-  #
-
-  def each_selected_installers
-    Dir.mkdir 'packages' unless File.dir?('packages')
-    @selected.each do |pack|
-      $stderr.puts "Processing the package `#{pack}' ..." if verbose?
-      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
-      Dir.chdir "packages/#{pack}"
-      yield @installers[pack]
-      Dir.chdir '../..'
-    end
-  end
-
-  def run_hook(id)
-    @root_installer.run_hook id
-  end
-
-  # module FileOperations requires this
-  def verbose?
-    @config.verbose?
-  end
-
-  # module FileOperations requires this
-  def no_harm?
-    @config.no_harm?
-  end
-
-end   # class ToplevelInstallerMulti
-
-
-class Installer
-
-  FILETYPES = %w( bin lib ext data conf man )
-
-  include FileOperations
-  include HookScriptAPI
-
-  def initialize(config, srcroot, objroot)
-    @config = config
-    @srcdir = File.expand_path(srcroot)
-    @objdir = File.expand_path(objroot)
-    @currdir = '.'
-  end
-
-  def inspect
-    "#<#{self.class} #{File.basename(@srcdir)}>"
-  end
-
-  def noop(rel)
-  end
-
-  #
-  # Hook Script API base methods
-  #
-
-  def srcdir_root
-    @srcdir
-  end
-
-  def objdir_root
-    @objdir
-  end
-
-  def relpath
-    @currdir
-  end
-
-  #
-  # Config Access
-  #
-
-  # module FileOperations requires this
-  def verbose?
-    @config.verbose?
-  end
-
-  # module FileOperations requires this
-  def no_harm?
-    @config.no_harm?
-  end
-
-  def verbose_off
-    begin
-      save, @config.verbose = @config.verbose?, false
-      yield
-    ensure
-      @config.verbose = save
-    end
-  end
-
-  #
-  # TASK config
-  #
-
-  def exec_config
-    exec_task_traverse 'config'
-  end
-
-  alias config_dir_bin noop
-  alias config_dir_lib noop
-
-  def config_dir_ext(rel)
-    extconf if extdir?(curr_srcdir())
-  end
-
-  alias config_dir_data noop
-  alias config_dir_conf noop
-  alias config_dir_man noop
-
-  def extconf
-    ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
-  end
-
-  #
-  # TASK setup
-  #
-
-  def exec_setup
-    exec_task_traverse 'setup'
-  end
-
-  def setup_dir_bin(rel)
-    files_of(curr_srcdir()).each do |fname|
-      update_shebang_line "#{curr_srcdir()}/#{fname}"
-    end
-  end
-
-  alias setup_dir_lib noop
-
-  def setup_dir_ext(rel)
-    make if extdir?(curr_srcdir())
-  end
-
-  alias setup_dir_data noop
-  alias setup_dir_conf noop
-  alias setup_dir_man noop
-
-  def update_shebang_line(path)
-    return if no_harm?
-    return if config('shebang') == 'never'
-    old = Shebang.load(path)
-    if old
-      $stderr.puts "warning: #{path}: Shebang line includes too many args.  It is not portable and your program may not work." if old.args.size > 1
-      new = new_shebang(old)
-      return if new.to_s == old.to_s
-    else
-      return unless config('shebang') == 'all'
-      new = Shebang.new(config('rubypath'))
-    end
-    $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
-    open_atomic_writer(path) {|output|
-      File.open(path, 'rb') {|f|
-        f.gets if old   # discard
-        output.puts new.to_s
-        output.print f.read
-      }
-    }
-  end
-
-  def new_shebang(old)
-    if /\Aruby/ =~ File.basename(old.cmd)
-      Shebang.new(config('rubypath'), old.args)
-    elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
-      Shebang.new(config('rubypath'), old.args[1..-1])
-    else
-      return old unless config('shebang') == 'all'
-      Shebang.new(config('rubypath'))
-    end
-  end
-
-  def open_atomic_writer(path, &block)
-    tmpfile = File.basename(path) + '.tmp'
-    begin
-      File.open(tmpfile, 'wb', &block)
-      File.rename tmpfile, File.basename(path)
-    ensure
-      File.unlink tmpfile if File.exist?(tmpfile)
-    end
-  end
-
-  class Shebang
-    def Shebang.load(path)
-      line = nil
-      File.open(path) {|f|
-        line = f.gets
-      }
-      return nil unless /\A#!/ =~ line
-      parse(line)
-    end
-
-    def Shebang.parse(line)
-      cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
-      new(cmd, args)
-    end
-
-    def initialize(cmd, args = [])
-      @cmd = cmd
-      @args = args
-    end
-
-    attr_reader :cmd
-    attr_reader :args
-
-    def to_s
-      "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
-    end
-  end
-
-  #
-  # TASK install
-  #
-
-  def exec_install
-    rm_f 'InstalledFiles'
-    exec_task_traverse 'install'
-  end
-
-  def install_dir_bin(rel)
-    install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
-  end
-
-  def install_dir_lib(rel)
-    install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
-  end
-
-  def install_dir_ext(rel)
-    return unless extdir?(curr_srcdir())
-    install_files rubyextentions('.'),
-                  "#{config('sodir')}/#{File.dirname(rel)}",
-                  0555
-  end
-
-  def install_dir_data(rel)
-    install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
-  end
-
-  def install_dir_conf(rel)
-    # FIXME: should not remove current config files
-    # (rename previous file to .old/.org)
-    install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
-  end
-
-  def install_dir_man(rel)
-    install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
-  end
-
-  def install_files(list, dest, mode)
-    mkdir_p dest, @config.install_prefix
-    list.each do |fname|
-      install fname, dest, mode, @config.install_prefix
-    end
-  end
-
-  def libfiles
-    glob_reject(%w(*.y *.output), targetfiles())
-  end
-
-  def rubyextentions(dir)
-    ents = glob_select("*.#{@config.dllext}", targetfiles())
-    if ents.empty?
-      setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
-    end
-    ents
-  end
-
-  def targetfiles
-    mapdir(existfiles() - hookfiles())
-  end
-
-  def mapdir(ents)
-    ents.map {|ent|
-      if File.exist?(ent)
-      then ent                         # objdir
-      else "#{curr_srcdir()}/#{ent}"   # srcdir
-      end
-    }
-  end
-
-  # picked up many entries from cvs-1.11.1/src/ignore.c
-  JUNK_FILES = %w( 
-    core RCSLOG tags TAGS .make.state
-    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
-    *~ *.old *.bak *.BAK *.orig *.rej _$* *$
-
-    *.org *.in .*
-  )
-
-  def existfiles
-    glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
-  end
-
-  def hookfiles
-    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
-      %w( config setup install clean ).map {|t| sprintf(fmt, t) }
-    }.flatten
-  end
-
-  def glob_select(pat, ents)
-    re = globs2re([pat])
-    ents.select {|ent| re =~ ent }
-  end
-
-  def glob_reject(pats, ents)
-    re = globs2re(pats)
-    ents.reject {|ent| re =~ ent }
-  end
-
-  GLOB2REGEX = {
-    '.' => '\.',
-    '$' => '\$',
-    '#' => '\#',
-    '*' => '.*'
-  }
-
-  def globs2re(pats)
-    /\A(?:#{
-      pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
-    })\z/
-  end
-
-  #
-  # TASK test
-  #
-
-  TESTDIR = 'test'
-
-  def exec_test
-    unless File.directory?('test')
-      $stderr.puts 'no test in this package' if verbose?
-      return
-    end
-    $stderr.puts 'Running tests...' if verbose?
-    begin
-      require 'test/unit'
-    rescue LoadError
-      setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'
-    end
-    runner = Test::Unit::AutoRunner.new(true)
-    runner.to_run << TESTDIR
-    runner.run
-  end
-
-  #
-  # TASK clean
-  #
-
-  def exec_clean
-    exec_task_traverse 'clean'
-    rm_f @config.savefile
-    rm_f 'InstalledFiles'
-  end
-
-  alias clean_dir_bin noop
-  alias clean_dir_lib noop
-  alias clean_dir_data noop
-  alias clean_dir_conf noop
-  alias clean_dir_man noop
-
-  def clean_dir_ext(rel)
-    return unless extdir?(curr_srcdir())
-    make 'clean' if File.file?('Makefile')
-  end
-
-  #
-  # TASK distclean
-  #
-
-  def exec_distclean
-    exec_task_traverse 'distclean'
-    rm_f @config.savefile
-    rm_f 'InstalledFiles'
-  end
-
-  alias distclean_dir_bin noop
-  alias distclean_dir_lib noop
-
-  def distclean_dir_ext(rel)
-    return unless extdir?(curr_srcdir())
-    make 'distclean' if File.file?('Makefile')
-  end
-
-  alias distclean_dir_data noop
-  alias distclean_dir_conf noop
-  alias distclean_dir_man noop
-
-  #
-  # Traversing
-  #
-
-  def exec_task_traverse(task)
-    run_hook "pre-#{task}"
-    FILETYPES.each do |type|
-      if type == 'ext' and config('without-ext') == 'yes'
-        $stderr.puts 'skipping ext/* by user option' if verbose?
-        next
-      end
-      traverse task, type, "#{task}_dir_#{type}"
-    end
-    run_hook "post-#{task}"
-  end
-
-  def traverse(task, rel, mid)
-    dive_into(rel) {
-      run_hook "pre-#{task}"
-      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
-      directories_of(curr_srcdir()).each do |d|
-        traverse task, "#{rel}/#{d}", mid
-      end
-      run_hook "post-#{task}"
-    }
-  end
-
-  def dive_into(rel)
-    return unless File.dir?("#{@srcdir}/#{rel}")
-
-    dir = File.basename(rel)
-    Dir.mkdir dir unless File.dir?(dir)
-    prevdir = Dir.pwd
-    Dir.chdir dir
-    $stderr.puts '---> ' + rel if verbose?
-    @currdir = rel
-    yield
-    Dir.chdir prevdir
-    $stderr.puts '<--- ' + rel if verbose?
-    @currdir = File.dirname(rel)
-  end
-
-  def run_hook(id)
-    path = [ "#{curr_srcdir()}/#{id}",
-             "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
-    return unless path
-    begin
-      instance_eval File.read(path), path, 1
-    rescue
-      raise if $DEBUG
-      setup_rb_error "hook #{path} failed:\n" + $!.message
-    end
-  end
-
-end   # class Installer
-
-
-class SetupError < StandardError; end
-
-def setup_rb_error(msg)
-  raise SetupError, msg
-end
-
-if $0 == __FILE__
-  begin
-    ToplevelInstaller.invoke
-  rescue SetupError
-    raise if $DEBUG
-    $stderr.puts $!.message
-    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
-    exit 1
-  end
-end
+#\r
+# setup.rb\r
+#\r
+# Copyright (c) 2000-2005 Minero Aoki\r
+#\r
+# This program is free software.\r
+# You can distribute/modify this program under the terms of\r
+# the GNU LGPL, Lesser General Public License version 2.1.\r
+#\r
+\r
+unless Enumerable.method_defined?(:map)   # Ruby 1.4.6\r
+  module Enumerable\r
+    alias map collect\r
+  end\r
+end\r
+\r
+unless File.respond_to?(:read)   # Ruby 1.6\r
+  def File.read(fname)\r
+    open(fname) {|f|\r
+      return f.read\r
+    }\r
+  end\r
+end\r
+\r
+unless Errno.const_defined?(:ENOTEMPTY)   # Windows?\r
+  module Errno\r
+    class ENOTEMPTY\r
+      # We do not raise this exception, implementation is not needed.\r
+    end\r
+  end\r
+end\r
+\r
+def File.binread(fname)\r
+  open(fname, 'rb') {|f|\r
+    return f.read\r
+  }\r
+end\r
+\r
+# for corrupted Windows' stat(2)\r
+def File.dir?(path)\r
+  File.directory?((path[-1,1] == '/') ? path : path + '/')\r
+end\r
+\r
+\r
+class ConfigTable\r
+\r
+  include Enumerable\r
+\r
+  def initialize(rbconfig)\r
+    @rbconfig = rbconfig\r
+    @items = []\r
+    @table = {}\r
+    # options\r
+    @install_prefix = nil\r
+    @config_opt = nil\r
+    @verbose = true\r
+    @no_harm = false\r
+  end\r
+\r
+  attr_accessor :install_prefix\r
+  attr_accessor :config_opt\r
+\r
+  attr_writer :verbose\r
+\r
+  def verbose?\r
+    @verbose\r
+  end\r
+\r
+  attr_writer :no_harm\r
+\r
+  def no_harm?\r
+    @no_harm\r
+  end\r
+\r
+  def [](key)\r
+    lookup(key).resolve(self)\r
+  end\r
+\r
+  def []=(key, val)\r
+    lookup(key).set val\r
+  end\r
+\r
+  def names\r
+    @items.map {|i| i.name }\r
+  end\r
+\r
+  def each(&block)\r
+    @items.each(&block)\r
+  end\r
+\r
+  def key?(name)\r
+    @table.key?(name)\r
+  end\r
+\r
+  def lookup(name)\r
+    @table[name] or setup_rb_error "no such config item: #{name}"\r
+  end\r
+\r
+  def add(item)\r
+    @items.push item\r
+    @table[item.name] = item\r
+  end\r
+\r
+  def remove(name)\r
+    item = lookup(name)\r
+    @items.delete_if {|i| i.name == name }\r
+    @table.delete_if {|name, i| i.name == name }\r
+    item\r
+  end\r
+\r
+  def load_script(path, inst = nil)\r
+    if File.file?(path)\r
+      MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path\r
+    end\r
+  end\r
+\r
+  def savefile\r
+    '.config'\r
+  end\r
+\r
+  def load_savefile\r
+    begin\r
+      File.foreach(savefile()) do |line|\r
+        k, v = *line.split(/=/, 2)\r
+        self[k] = v.strip\r
+      end\r
+    rescue Errno::ENOENT\r
+      setup_rb_error $!.message + "\n#{File.basename($0)} config first"\r
+    end\r
+  end\r
+\r
+  def save\r
+    @items.each {|i| i.value }\r
+    File.open(savefile(), 'w') {|f|\r
+      @items.each do |i|\r
+        f.printf "%s=%s\n", i.name, i.value if i.value? and i.value\r
+      end\r
+    }\r
+  end\r
+\r
+  def load_standard_entries\r
+    standard_entries(@rbconfig).each do |ent|\r
+      add ent\r
+    end\r
+  end\r
+\r
+  def standard_entries(rbconfig)\r
+    c = rbconfig\r
+\r
+    rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])\r
+\r
+    major = c['MAJOR'].to_i\r
+    minor = c['MINOR'].to_i\r
+    teeny = c['TEENY'].to_i\r
+    version = "#{major}.#{minor}"\r
+\r
+    # ruby ver. >= 1.4.4?\r
+    newpath_p = ((major >= 2) or\r
+                 ((major == 1) and\r
+                  ((minor >= 5) or\r
+                   ((minor == 4) and (teeny >= 4)))))\r
+\r
+    if c['rubylibdir']\r
+      # V > 1.6.3\r
+      libruby         = "#{c['prefix']}/lib/ruby"\r
+      librubyver      = c['rubylibdir']\r
+      librubyverarch  = c['archdir']\r
+      siteruby        = c['sitedir']\r
+      siterubyver     = c['sitelibdir']\r
+      siterubyverarch = c['sitearchdir']\r
+    elsif newpath_p\r
+      # 1.4.4 <= V <= 1.6.3\r
+      libruby         = "#{c['prefix']}/lib/ruby"\r
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"\r
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"\r
+      siteruby        = c['sitedir']\r
+      siterubyver     = "$siteruby/#{version}"\r
+      siterubyverarch = "$siterubyver/#{c['arch']}"\r
+    else\r
+      # V < 1.4.4\r
+      libruby         = "#{c['prefix']}/lib/ruby"\r
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"\r
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"\r
+      siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"\r
+      siterubyver     = siteruby\r
+      siterubyverarch = "$siterubyver/#{c['arch']}"\r
+    end\r
+    parameterize = lambda {|path|\r
+      path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\r
+    }\r
+\r
+    if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }\r
+      makeprog = arg.sub(/'/, '').split(/=/, 2)[1]\r
+    else\r
+      makeprog = 'make'\r
+    end\r
+\r
+    [\r
+      ExecItem.new('installdirs', 'std/site/home',\r
+                   'std: install under libruby; site: install under site_ruby; home: install under $HOME')\\r
+          {|val, table|\r
+            case val\r
+            when 'std'\r
+              table['rbdir'] = '$librubyver'\r
+              table['sodir'] = '$librubyverarch'\r
+            when 'site'\r
+              table['rbdir'] = '$siterubyver'\r
+              table['sodir'] = '$siterubyverarch'\r
+            when 'home'\r
+              setup_rb_error '$HOME was not set' unless ENV['HOME']\r
+              table['prefix'] = ENV['HOME']\r
+              table['rbdir'] = '$libdir/ruby'\r
+              table['sodir'] = '$libdir/ruby'\r
+            end\r
+          },\r
+      PathItem.new('prefix', 'path', c['prefix'],\r
+                   'path prefix of target environment'),\r
+      PathItem.new('bindir', 'path', parameterize.call(c['bindir']),\r
+                   'the directory for commands'),\r
+      PathItem.new('libdir', 'path', parameterize.call(c['libdir']),\r
+                   'the directory for libraries'),\r
+      PathItem.new('datadir', 'path', parameterize.call(c['datadir']),\r
+                   'the directory for shared data'),\r
+      PathItem.new('mandir', 'path', parameterize.call(c['mandir']),\r
+                   'the directory for man pages'),\r
+      PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),\r
+                   'the directory for system configuration files'),\r
+      PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),\r
+                   'the directory for local state data'),\r
+      PathItem.new('libruby', 'path', libruby,\r
+                   'the directory for ruby libraries'),\r
+      PathItem.new('librubyver', 'path', librubyver,\r
+                   'the directory for standard ruby libraries'),\r
+      PathItem.new('librubyverarch', 'path', librubyverarch,\r
+                   'the directory for standard ruby extensions'),\r
+      PathItem.new('siteruby', 'path', siteruby,\r
+          'the directory for version-independent aux ruby libraries'),\r
+      PathItem.new('siterubyver', 'path', siterubyver,\r
+                   'the directory for aux ruby libraries'),\r
+      PathItem.new('siterubyverarch', 'path', siterubyverarch,\r
+                   'the directory for aux ruby binaries'),\r
+      PathItem.new('rbdir', 'path', '$siterubyver',\r
+                   'the directory for ruby scripts'),\r
+      PathItem.new('sodir', 'path', '$siterubyverarch',\r
+                   'the directory for ruby extentions'),\r
+      PathItem.new('rubypath', 'path', rubypath,\r
+                   'the path to set to #! line'),\r
+      ProgramItem.new('rubyprog', 'name', rubypath,\r
+                      'the ruby program using for installation'),\r
+      ProgramItem.new('makeprog', 'name', makeprog,\r
+                      'the make program to compile ruby extentions'),\r
+      SelectItem.new('shebang', 'all/ruby/never', 'ruby',\r
+                     'shebang line (#!) editing mode'),\r
+      BoolItem.new('without-ext', 'yes/no', 'no',\r
+                   'does not compile/install ruby extentions')\r
+    ]\r
+  end\r
+  private :standard_entries\r
+\r
+  def load_multipackage_entries\r
+    multipackage_entries().each do |ent|\r
+      add ent\r
+    end\r
+  end\r
+\r
+  def multipackage_entries\r
+    [\r
+      PackageSelectionItem.new('with', 'name,name...', '', 'ALL',\r
+                               'package names that you want to install'),\r
+      PackageSelectionItem.new('without', 'name,name...', '', 'NONE',\r
+                               'package names that you do not want to install')\r
+    ]\r
+  end\r
+  private :multipackage_entries\r
+\r
+  ALIASES = {\r
+    'std-ruby'         => 'librubyver',\r
+    'stdruby'          => 'librubyver',\r
+    'rubylibdir'       => 'librubyver',\r
+    'archdir'          => 'librubyverarch',\r
+    'site-ruby-common' => 'siteruby',     # For backward compatibility\r
+    'site-ruby'        => 'siterubyver',  # For backward compatibility\r
+    'bin-dir'          => 'bindir',\r
+    'bin-dir'          => 'bindir',\r
+    'rb-dir'           => 'rbdir',\r
+    'so-dir'           => 'sodir',\r
+    'data-dir'         => 'datadir',\r
+    'ruby-path'        => 'rubypath',\r
+    'ruby-prog'        => 'rubyprog',\r
+    'ruby'             => 'rubyprog',\r
+    'make-prog'        => 'makeprog',\r
+    'make'             => 'makeprog'\r
+  }\r
+\r
+  def fixup\r
+    ALIASES.each do |ali, name|\r
+      @table[ali] = @table[name]\r
+    end\r
+    @items.freeze\r
+    @table.freeze\r
+    @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/\r
+  end\r
+\r
+  def parse_opt(opt)\r
+    m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"\r
+    m.to_a[1,2]\r
+  end\r
+\r
+  def dllext\r
+    @rbconfig['DLEXT']\r
+  end\r
+\r
+  def value_config?(name)\r
+    lookup(name).value?\r
+  end\r
+\r
+  class Item\r
+    def initialize(name, template, default, desc)\r
+      @name = name.freeze\r
+      @template = template\r
+      @value = default\r
+      @default = default\r
+      @description = desc\r
+    end\r
+\r
+    attr_reader :name\r
+    attr_reader :description\r
+\r
+    attr_accessor :default\r
+    alias help_default default\r
+\r
+    def help_opt\r
+      "--#{@name}=#{@template}"\r
+    end\r
+\r
+    def value?\r
+      true\r
+    end\r
+\r
+    def value\r
+      @value\r
+    end\r
+\r
+    def resolve(table)\r
+      @value.gsub(%r<\$([^/]+)>) { table[$1] }\r
+    end\r
+\r
+    def set(val)\r
+      @value = check(val)\r
+    end\r
+\r
+    private\r
+\r
+    def check(val)\r
+      setup_rb_error "config: --#{name} requires argument" unless val\r
+      val\r
+    end\r
+  end\r
+\r
+  class BoolItem < Item\r
+    def config_type\r
+      'bool'\r
+    end\r
+\r
+    def help_opt\r
+      "--#{@name}"\r
+    end\r
+\r
+    private\r
+\r
+    def check(val)\r
+      return 'yes' unless val\r
+      case val\r
+      when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'\r
+      when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'\r
+      else\r
+        setup_rb_error "config: --#{@name} accepts only yes/no for argument"\r
+      end\r
+    end\r
+  end\r
+\r
+  class PathItem < Item\r
+    def config_type\r
+      'path'\r
+    end\r
+\r
+    private\r
+\r
+    def check(path)\r
+      setup_rb_error "config: --#{@name} requires argument"  unless path\r
+      path[0,1] == '$' ? path : File.expand_path(path)\r
+    end\r
+  end\r
+\r
+  class ProgramItem < Item\r
+    def config_type\r
+      'program'\r
+    end\r
+  end\r
+\r
+  class SelectItem < Item\r
+    def initialize(name, selection, default, desc)\r
+      super\r
+      @ok = selection.split('/')\r
+    end\r
+\r
+    def config_type\r
+      'select'\r
+    end\r
+\r
+    private\r
+\r
+    def check(val)\r
+      unless @ok.include?(val.strip)\r
+        setup_rb_error "config: use --#{@name}=#{@template} (#{val})"\r
+      end\r
+      val.strip\r
+    end\r
+  end\r
+\r
+  class ExecItem < Item\r
+    def initialize(name, selection, desc, &block)\r
+      super name, selection, nil, desc\r
+      @ok = selection.split('/')\r
+      @action = block\r
+    end\r
+\r
+    def config_type\r
+      'exec'\r
+    end\r
+\r
+    def value?\r
+      false\r
+    end\r
+\r
+    def resolve(table)\r
+      setup_rb_error "$#{name()} wrongly used as option value"\r
+    end\r
+\r
+    undef set\r
+\r
+    def evaluate(val, table)\r
+      v = val.strip.downcase\r
+      unless @ok.include?(v)\r
+        setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"\r
+      end\r
+      @action.call v, table\r
+    end\r
+  end\r
+\r
+  class PackageSelectionItem < Item\r
+    def initialize(name, template, default, help_default, desc)\r
+      super name, template, default, desc\r
+      @help_default = help_default\r
+    end\r
+\r
+    attr_reader :help_default\r
+\r
+    def config_type\r
+      'package'\r
+    end\r
+\r
+    private\r
+\r
+    def check(val)\r
+      unless File.dir?("packages/#{val}")\r
+        setup_rb_error "config: no such package: #{val}"\r
+      end\r
+      val\r
+    end\r
+  end\r
+\r
+  class MetaConfigEnvironment\r
+    def initialize(config, installer)\r
+      @config = config\r
+      @installer = installer\r
+    end\r
+\r
+    def config_names\r
+      @config.names\r
+    end\r
+\r
+    def config?(name)\r
+      @config.key?(name)\r
+    end\r
+\r
+    def bool_config?(name)\r
+      @config.lookup(name).config_type == 'bool'\r
+    end\r
+\r
+    def path_config?(name)\r
+      @config.lookup(name).config_type == 'path'\r
+    end\r
+\r
+    def value_config?(name)\r
+      @config.lookup(name).config_type != 'exec'\r
+    end\r
+\r
+    def add_config(item)\r
+      @config.add item\r
+    end\r
+\r
+    def add_bool_config(name, default, desc)\r
+      @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)\r
+    end\r
+\r
+    def add_path_config(name, default, desc)\r
+      @config.add PathItem.new(name, 'path', default, desc)\r
+    end\r
+\r
+    def set_config_default(name, default)\r
+      @config.lookup(name).default = default\r
+    end\r
+\r
+    def remove_config(name)\r
+      @config.remove(name)\r
+    end\r
+\r
+    # For only multipackage\r
+    def packages\r
+      raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer\r
+      @installer.packages\r
+    end\r
+\r
+    # For only multipackage\r
+    def declare_packages(list)\r
+      raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer\r
+      @installer.packages = list\r
+    end\r
+  end\r
+\r
+end   # class ConfigTable\r
+\r
+\r
+# This module requires: #verbose?, #no_harm?\r
+module FileOperations\r
+\r
+  def mkdir_p(dirname, prefix = nil)\r
+    dirname = prefix + File.expand_path(dirname) if prefix\r
+    $stderr.puts "mkdir -p #{dirname}" if verbose?\r
+    return if no_harm?\r
+\r
+    # Does not check '/', it's too abnormal.\r
+    dirs = File.expand_path(dirname).split(%r<(?=/)>)\r
+    if /\A[a-z]:\z/i =~ dirs[0]\r
+      disk = dirs.shift\r
+      dirs[0] = disk + dirs[0]\r
+    end\r
+    dirs.each_index do |idx|\r
+      path = dirs[0..idx].join('')\r
+      Dir.mkdir path unless File.dir?(path)\r
+    end\r
+  end\r
+\r
+  def rm_f(path)\r
+    $stderr.puts "rm -f #{path}" if verbose?\r
+    return if no_harm?\r
+    force_remove_file path\r
+  end\r
+\r
+  def rm_rf(path)\r
+    $stderr.puts "rm -rf #{path}" if verbose?\r
+    return if no_harm?\r
+    remove_tree path\r
+  end\r
+\r
+  def remove_tree(path)\r
+    if File.symlink?(path)\r
+      remove_file path\r
+    elsif File.dir?(path)\r
+      remove_tree0 path\r
+    else\r
+      force_remove_file path\r
+    end\r
+  end\r
+\r
+  def remove_tree0(path)\r
+    Dir.foreach(path) do |ent|\r
+      next if ent == '.'\r
+      next if ent == '..'\r
+      entpath = "#{path}/#{ent}"\r
+      if File.symlink?(entpath)\r
+        remove_file entpath\r
+      elsif File.dir?(entpath)\r
+        remove_tree0 entpath\r
+      else\r
+        force_remove_file entpath\r
+      end\r
+    end\r
+    begin\r
+      Dir.rmdir path\r
+    rescue Errno::ENOTEMPTY\r
+      # directory may not be empty\r
+    end\r
+  end\r
+\r
+  def move_file(src, dest)\r
+    force_remove_file dest\r
+    begin\r
+      File.rename src, dest\r
+    rescue\r
+      File.open(dest, 'wb') {|f|\r
+        f.write File.binread(src)\r
+      }\r
+      File.chmod File.stat(src).mode, dest\r
+      File.unlink src\r
+    end\r
+  end\r
+\r
+  def force_remove_file(path)\r
+    begin\r
+      remove_file path\r
+    rescue\r
+    end\r
+  end\r
+\r
+  def remove_file(path)\r
+    File.chmod 0777, path\r
+    File.unlink path\r
+  end\r
+\r
+  def install(from, dest, mode, prefix = nil)\r
+    $stderr.puts "install #{from} #{dest}" if verbose?\r
+    return if no_harm?\r
+\r
+    realdest = prefix ? prefix + File.expand_path(dest) : dest\r
+    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)\r
+    str = File.binread(from)\r
+    if diff?(str, realdest)\r
+      verbose_off {\r
+        rm_f realdest if File.exist?(realdest)\r
+      }\r
+      File.open(realdest, 'wb') {|f|\r
+        f.write str\r
+      }\r
+      File.chmod mode, realdest\r
+\r
+      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|\r
+        if prefix\r
+          f.puts realdest.sub(prefix, '')\r
+        else\r
+          f.puts realdest\r
+        end\r
+      }\r
+    end\r
+  end\r
+\r
+  def diff?(new_content, path)\r
+    return true unless File.exist?(path)\r
+    new_content != File.binread(path)\r
+  end\r
+\r
+  def command(*args)\r
+    $stderr.puts args.join(' ') if verbose?\r
+    system(*args) or raise RuntimeError,\r
+        "system(#{args.map{|a| a.inspect }.join(' ')}) failed"\r
+  end\r
+\r
+  def ruby(*args)\r
+    command config('rubyprog'), *args\r
+  end\r
+  \r
+  def make(task = nil)\r
+    command(*[config('makeprog'), task].compact)\r
+  end\r
+\r
+  def extdir?(dir)\r
+    File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")\r
+  end\r
+\r
+  def files_of(dir)\r
+    Dir.open(dir) {|d|\r
+      return d.select {|ent| File.file?("#{dir}/#{ent}") }\r
+    }\r
+  end\r
+\r
+  DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )\r
+\r
+  def directories_of(dir)\r
+    Dir.open(dir) {|d|\r
+      return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT\r
+    }\r
+  end\r
+\r
+end\r
+\r
+\r
+# This module requires: #srcdir_root, #objdir_root, #relpath\r
+module HookScriptAPI\r
+\r
+  def get_config(key)\r
+    @config[key]\r
+  end\r
+\r
+  alias config get_config\r
+\r
+  # obsolete: use metaconfig to change configuration\r
+  def set_config(key, val)\r
+    @config[key] = val\r
+  end\r
+\r
+  #\r
+  # srcdir/objdir (works only in the package directory)\r
+  #\r
+\r
+  def curr_srcdir\r
+    "#{srcdir_root()}/#{relpath()}"\r
+  end\r
+\r
+  def curr_objdir\r
+    "#{objdir_root()}/#{relpath()}"\r
+  end\r
+\r
+  def srcfile(path)\r
+    "#{curr_srcdir()}/#{path}"\r
+  end\r
+\r
+  def srcexist?(path)\r
+    File.exist?(srcfile(path))\r
+  end\r
+\r
+  def srcdirectory?(path)\r
+    File.dir?(srcfile(path))\r
+  end\r
+  \r
+  def srcfile?(path)\r
+    File.file?(srcfile(path))\r
+  end\r
+\r
+  def srcentries(path = '.')\r
+    Dir.open("#{curr_srcdir()}/#{path}") {|d|\r
+      return d.to_a - %w(. ..)\r
+    }\r
+  end\r
+\r
+  def srcfiles(path = '.')\r
+    srcentries(path).select {|fname|\r
+      File.file?(File.join(curr_srcdir(), path, fname))\r
+    }\r
+  end\r
+\r
+  def srcdirectories(path = '.')\r
+    srcentries(path).select {|fname|\r
+      File.dir?(File.join(curr_srcdir(), path, fname))\r
+    }\r
+  end\r
+\r
+end\r
+\r
+\r
+class ToplevelInstaller\r
+\r
+  Version   = '3.4.1'\r
+  Copyright = 'Copyright (c) 2000-2005 Minero Aoki'\r
+\r
+  TASKS = [\r
+    [ 'all',      'do config, setup, then install' ],\r
+    [ 'config',   'saves your configurations' ],\r
+    [ 'show',     'shows current configuration' ],\r
+    [ 'setup',    'compiles ruby extentions and others' ],\r
+    [ 'install',  'installs files' ],\r
+    [ 'test',     'run all tests in test/' ],\r
+    [ 'clean',    "does `make clean' for each extention" ],\r
+    [ 'distclean',"does `make distclean' for each extention" ]\r
+  ]\r
+\r
+  def ToplevelInstaller.invoke\r
+    config = ConfigTable.new(load_rbconfig())\r
+    config.load_standard_entries\r
+    config.load_multipackage_entries if multipackage?\r
+    config.fixup\r
+    klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)\r
+    klass.new(File.dirname($0), config).invoke\r
+  end\r
+\r
+  def ToplevelInstaller.multipackage?\r
+    File.dir?(File.dirname($0) + '/packages')\r
+  end\r
+\r
+  def ToplevelInstaller.load_rbconfig\r
+    if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }\r
+      ARGV.delete(arg)\r
+      load File.expand_path(arg.split(/=/, 2)[1])\r
+      $".push 'rbconfig.rb'\r
+    else\r
+      require 'rbconfig'\r
+    end\r
+    ::Config::CONFIG\r
+  end\r
+\r
+  def initialize(ardir_root, config)\r
+    @ardir = File.expand_path(ardir_root)\r
+    @config = config\r
+    # cache\r
+    @valid_task_re = nil\r
+  end\r
+\r
+  def config(key)\r
+    @config[key]\r
+  end\r
+\r
+  def inspect\r
+    "#<#{self.class} #{__id__()}>"\r
+  end\r
+\r
+  def invoke\r
+    run_metaconfigs\r
+    case task = parsearg_global()\r
+    when nil, 'all'\r
+      parsearg_config\r
+      init_installers\r
+      exec_config\r
+      exec_setup\r
+      exec_install\r
+    else\r
+      case task\r
+      when 'config', 'test'\r
+        ;\r
+      when 'clean', 'distclean'\r
+        @config.load_savefile if File.exist?(@config.savefile)\r
+      else\r
+        @config.load_savefile\r
+      end\r
+      __send__ "parsearg_#{task}"\r
+      init_installers\r
+      __send__ "exec_#{task}"\r
+    end\r
+  end\r
+  \r
+  def run_metaconfigs\r
+    @config.load_script "#{@ardir}/metaconfig"\r
+  end\r
+\r
+  def init_installers\r
+    @installer = Installer.new(@config, @ardir, File.expand_path('.'))\r
+  end\r
+\r
+  #\r
+  # Hook Script API bases\r
+  #\r
+\r
+  def srcdir_root\r
+    @ardir\r
+  end\r
+\r
+  def objdir_root\r
+    '.'\r
+  end\r
+\r
+  def relpath\r
+    '.'\r
+  end\r
+\r
+  #\r
+  # Option Parsing\r
+  #\r
+\r
+  def parsearg_global\r
+    while arg = ARGV.shift\r
+      case arg\r
+      when /\A\w+\z/\r
+        setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)\r
+        return arg\r
+      when '-q', '--quiet'\r
+        @config.verbose = false\r
+      when '--verbose'\r
+        @config.verbose = true\r
+      when '--help'\r
+        print_usage $stdout\r
+        exit 0\r
+      when '--version'\r
+        puts "#{File.basename($0)} version #{Version}"\r
+        exit 0\r
+      when '--copyright'\r
+        puts Copyright\r
+        exit 0\r
+      else\r
+        setup_rb_error "unknown global option '#{arg}'"\r
+      end\r
+    end\r
+    nil\r
+  end\r
+\r
+  def valid_task?(t)\r
+    valid_task_re() =~ t\r
+  end\r
+\r
+  def valid_task_re\r
+    @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/\r
+  end\r
+\r
+  def parsearg_no_options\r
+    unless ARGV.empty?\r
+      task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)\r
+      setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"\r
+    end\r
+  end\r
+\r
+  alias parsearg_show       parsearg_no_options\r
+  alias parsearg_setup      parsearg_no_options\r
+  alias parsearg_test       parsearg_no_options\r
+  alias parsearg_clean      parsearg_no_options\r
+  alias parsearg_distclean  parsearg_no_options\r
+\r
+  def parsearg_config\r
+    evalopt = []\r
+    set = []\r
+    @config.config_opt = []\r
+    while i = ARGV.shift\r
+      if /\A--?\z/ =~ i\r
+        @config.config_opt = ARGV.dup\r
+        break\r
+      end\r
+      name, value = *@config.parse_opt(i)\r
+      if @config.value_config?(name)\r
+        @config[name] = value\r
+      else\r
+        evalopt.push [name, value]\r
+      end\r
+      set.push name\r
+    end\r
+    evalopt.each do |name, value|\r
+      @config.lookup(name).evaluate value, @config\r
+    end\r
+    # Check if configuration is valid\r
+    set.each do |n|\r
+      @config[n] if @config.value_config?(n)\r
+    end\r
+  end\r
+\r
+  def parsearg_install\r
+    @config.no_harm = false\r
+    @config.install_prefix = ''\r
+    while a = ARGV.shift\r
+      case a\r
+      when '--no-harm'\r
+        @config.no_harm = true\r
+      when /\A--prefix=/\r
+        path = a.split(/=/, 2)[1]\r
+        path = File.expand_path(path) unless path[0,1] == '/'\r
+        @config.install_prefix = path\r
+      else\r
+        setup_rb_error "install: unknown option #{a}"\r
+      end\r
+    end\r
+  end\r
+\r
+  def print_usage(out)\r
+    out.puts 'Typical Installation Procedure:'\r
+    out.puts "  $ ruby #{File.basename $0} config"\r
+    out.puts "  $ ruby #{File.basename $0} setup"\r
+    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"\r
+    out.puts\r
+    out.puts 'Detailed Usage:'\r
+    out.puts "  ruby #{File.basename $0} <global option>"\r
+    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"\r
+\r
+    fmt = "  %-24s %s\n"\r
+    out.puts\r
+    out.puts 'Global options:'\r
+    out.printf fmt, '-q,--quiet',   'suppress message outputs'\r
+    out.printf fmt, '   --verbose', 'output messages verbosely'\r
+    out.printf fmt, '   --help',    'print this message'\r
+    out.printf fmt, '   --version', 'print version and quit'\r
+    out.printf fmt, '   --copyright',  'print copyright and quit'\r
+    out.puts\r
+    out.puts 'Tasks:'\r
+    TASKS.each do |name, desc|\r
+      out.printf fmt, name, desc\r
+    end\r
+\r
+    fmt = "  %-24s %s [%s]\n"\r
+    out.puts\r
+    out.puts 'Options for CONFIG or ALL:'\r
+    @config.each do |item|\r
+      out.printf fmt, item.help_opt, item.description, item.help_default\r
+    end\r
+    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"\r
+    out.puts\r
+    out.puts 'Options for INSTALL:'\r
+    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'\r
+    out.printf fmt, '--prefix=path',  'install path prefix', ''\r
+    out.puts\r
+  end\r
+\r
+  #\r
+  # Task Handlers\r
+  #\r
+\r
+  def exec_config\r
+    @installer.exec_config\r
+    @config.save   # must be final\r
+  end\r
+\r
+  def exec_setup\r
+    @installer.exec_setup\r
+  end\r
+\r
+  def exec_install\r
+    @installer.exec_install\r
+  end\r
+\r
+  def exec_test\r
+    @installer.exec_test\r
+  end\r
+\r
+  def exec_show\r
+    @config.each do |i|\r
+      printf "%-20s %s\n", i.name, i.value if i.value?\r
+    end\r
+  end\r
+\r
+  def exec_clean\r
+    @installer.exec_clean\r
+  end\r
+\r
+  def exec_distclean\r
+    @installer.exec_distclean\r
+  end\r
+\r
+end   # class ToplevelInstaller\r
+\r
+\r
+class ToplevelInstallerMulti < ToplevelInstaller\r
+\r
+  include FileOperations\r
+\r
+  def initialize(ardir_root, config)\r
+    super\r
+    @packages = directories_of("#{@ardir}/packages")\r
+    raise 'no package exists' if @packages.empty?\r
+    @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))\r
+  end\r
+\r
+  def run_metaconfigs\r
+    @config.load_script "#{@ardir}/metaconfig", self\r
+    @packages.each do |name|\r
+      @config.load_script "#{@ardir}/packages/#{name}/metaconfig"\r
+    end\r
+  end\r
+\r
+  attr_reader :packages\r
+\r
+  def packages=(list)\r
+    raise 'package list is empty' if list.empty?\r
+    list.each do |name|\r
+      raise "directory packages/#{name} does not exist"\\r
+              unless File.dir?("#{@ardir}/packages/#{name}")\r
+    end\r
+    @packages = list\r
+  end\r
+\r
+  def init_installers\r
+    @installers = {}\r
+    @packages.each do |pack|\r
+      @installers[pack] = Installer.new(@config,\r
+                                       "#{@ardir}/packages/#{pack}",\r
+                                       "packages/#{pack}")\r
+    end\r
+    with    = extract_selection(config('with'))\r
+    without = extract_selection(config('without'))\r
+    @selected = @installers.keys.select {|name|\r
+                  (with.empty? or with.include?(name)) \\r
+                      and not without.include?(name)\r
+                }\r
+  end\r
+\r
+  def extract_selection(list)\r
+    a = list.split(/,/)\r
+    a.each do |name|\r
+      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)\r
+    end\r
+    a\r
+  end\r
+\r
+  def print_usage(f)\r
+    super\r
+    f.puts 'Inluded packages:'\r
+    f.puts '  ' + @packages.sort.join(' ')\r
+    f.puts\r
+  end\r
+\r
+  #\r
+  # Task Handlers\r
+  #\r
+\r
+  def exec_config\r
+    run_hook 'pre-config'\r
+    each_selected_installers {|inst| inst.exec_config }\r
+    run_hook 'post-config'\r
+    @config.save   # must be final\r
+  end\r
+\r
+  def exec_setup\r
+    run_hook 'pre-setup'\r
+    each_selected_installers {|inst| inst.exec_setup }\r
+    run_hook 'post-setup'\r
+  end\r
+\r
+  def exec_install\r
+    run_hook 'pre-install'\r
+    each_selected_installers {|inst| inst.exec_install }\r
+    run_hook 'post-install'\r
+  end\r
+\r
+  def exec_test\r
+    run_hook 'pre-test'\r
+    each_selected_installers {|inst| inst.exec_test }\r
+    run_hook 'post-test'\r
+  end\r
+\r
+  def exec_clean\r
+    rm_f @config.savefile\r
+    run_hook 'pre-clean'\r
+    each_selected_installers {|inst| inst.exec_clean }\r
+    run_hook 'post-clean'\r
+  end\r
+\r
+  def exec_distclean\r
+    rm_f @config.savefile\r
+    run_hook 'pre-distclean'\r
+    each_selected_installers {|inst| inst.exec_distclean }\r
+    run_hook 'post-distclean'\r
+  end\r
+\r
+  #\r
+  # lib\r
+  #\r
+\r
+  def each_selected_installers\r
+    Dir.mkdir 'packages' unless File.dir?('packages')\r
+    @selected.each do |pack|\r
+      $stderr.puts "Processing the package `#{pack}' ..." if verbose?\r
+      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")\r
+      Dir.chdir "packages/#{pack}"\r
+      yield @installers[pack]\r
+      Dir.chdir '../..'\r
+    end\r
+  end\r
+\r
+  def run_hook(id)\r
+    @root_installer.run_hook id\r
+  end\r
+\r
+  # module FileOperations requires this\r
+  def verbose?\r
+    @config.verbose?\r
+  end\r
+\r
+  # module FileOperations requires this\r
+  def no_harm?\r
+    @config.no_harm?\r
+  end\r
+\r
+end   # class ToplevelInstallerMulti\r
+\r
+\r
+class Installer\r
+\r
+  FILETYPES = %w( bin lib ext data conf man )\r
+\r
+  include FileOperations\r
+  include HookScriptAPI\r
+\r
+  def initialize(config, srcroot, objroot)\r
+    @config = config\r
+    @srcdir = File.expand_path(srcroot)\r
+    @objdir = File.expand_path(objroot)\r
+    @currdir = '.'\r
+  end\r
+\r
+  def inspect\r
+    "#<#{self.class} #{File.basename(@srcdir)}>"\r
+  end\r
+\r
+  def noop(rel)\r
+  end\r
+\r
+  #\r
+  # Hook Script API base methods\r
+  #\r
+\r
+  def srcdir_root\r
+    @srcdir\r
+  end\r
+\r
+  def objdir_root\r
+    @objdir\r
+  end\r
+\r
+  def relpath\r
+    @currdir\r
+  end\r
+\r
+  #\r
+  # Config Access\r
+  #\r
+\r
+  # module FileOperations requires this\r
+  def verbose?\r
+    @config.verbose?\r
+  end\r
+\r
+  # module FileOperations requires this\r
+  def no_harm?\r
+    @config.no_harm?\r
+  end\r
+\r
+  def verbose_off\r
+    begin\r
+      save, @config.verbose = @config.verbose?, false\r
+      yield\r
+    ensure\r
+      @config.verbose = save\r
+    end\r
+  end\r
+\r
+  #\r
+  # TASK config\r
+  #\r
+\r
+  def exec_config\r
+    exec_task_traverse 'config'\r
+  end\r
+\r
+  alias config_dir_bin noop\r
+  alias config_dir_lib noop\r
+\r
+  def config_dir_ext(rel)\r
+    extconf if extdir?(curr_srcdir())\r
+  end\r
+\r
+  alias config_dir_data noop\r
+  alias config_dir_conf noop\r
+  alias config_dir_man noop\r
+\r
+  def extconf\r
+    ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt\r
+  end\r
+\r
+  #\r
+  # TASK setup\r
+  #\r
+\r
+  def exec_setup\r
+    exec_task_traverse 'setup'\r
+  end\r
+\r
+  def setup_dir_bin(rel)\r
+    files_of(curr_srcdir()).each do |fname|\r
+      update_shebang_line "#{curr_srcdir()}/#{fname}"\r
+    end\r
+  end\r
+\r
+  alias setup_dir_lib noop\r
+\r
+  def setup_dir_ext(rel)\r
+    make if extdir?(curr_srcdir())\r
+  end\r
+\r
+  alias setup_dir_data noop\r
+  alias setup_dir_conf noop\r
+  alias setup_dir_man noop\r
+\r
+  def update_shebang_line(path)\r
+    return if no_harm?\r
+    return if config('shebang') == 'never'\r
+    old = Shebang.load(path)\r
+    if old\r
+      $stderr.puts "warning: #{path}: Shebang line includes too many args.  It is not portable and your program may not work." if old.args.size > 1\r
+      new = new_shebang(old)\r
+      return if new.to_s == old.to_s\r
+    else\r
+      return unless config('shebang') == 'all'\r
+      new = Shebang.new(config('rubypath'))\r
+    end\r
+    $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?\r
+    open_atomic_writer(path) {|output|\r
+      File.open(path, 'rb') {|f|\r
+        f.gets if old   # discard\r
+        output.puts new.to_s\r
+        output.print f.read\r
+      }\r
+    }\r
+  end\r
+\r
+  def new_shebang(old)\r
+    if /\Aruby/ =~ File.basename(old.cmd)\r
+      Shebang.new(config('rubypath'), old.args)\r
+    elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'\r
+      Shebang.new(config('rubypath'), old.args[1..-1])\r
+    else\r
+      return old unless config('shebang') == 'all'\r
+      Shebang.new(config('rubypath'))\r
+    end\r
+  end\r
+\r
+  def open_atomic_writer(path, &block)\r
+    tmpfile = File.basename(path) + '.tmp'\r
+    begin\r
+      File.open(tmpfile, 'wb', &block)\r
+      File.rename tmpfile, File.basename(path)\r
+    ensure\r
+      File.unlink tmpfile if File.exist?(tmpfile)\r
+    end\r
+  end\r
+\r
+  class Shebang\r
+    def Shebang.load(path)\r
+      line = nil\r
+      File.open(path) {|f|\r
+        line = f.gets\r
+      }\r
+      return nil unless /\A#!/ =~ line\r
+      parse(line)\r
+    end\r
+\r
+    def Shebang.parse(line)\r
+      cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')\r
+      new(cmd, args)\r
+    end\r
+\r
+    def initialize(cmd, args = [])\r
+      @cmd = cmd\r
+      @args = args\r
+    end\r
+\r
+    attr_reader :cmd\r
+    attr_reader :args\r
+\r
+    def to_s\r
+      "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")\r
+    end\r
+  end\r
+\r
+  #\r
+  # TASK install\r
+  #\r
+\r
+  def exec_install\r
+    rm_f 'InstalledFiles'\r
+    exec_task_traverse 'install'\r
+  end\r
+\r
+  def install_dir_bin(rel)\r
+    install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755\r
+  end\r
+\r
+  def install_dir_lib(rel)\r
+    install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644\r
+  end\r
+\r
+  def install_dir_ext(rel)\r
+    return unless extdir?(curr_srcdir())\r
+    install_files rubyextentions('.'),\r
+                  "#{config('sodir')}/#{File.dirname(rel)}",\r
+                  0555\r
+  end\r
+\r
+  def install_dir_data(rel)\r
+    install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644\r
+  end\r
+\r
+  def install_dir_conf(rel)\r
+    # FIXME: should not remove current config files\r
+    # (rename previous file to .old/.org)\r
+    install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644\r
+  end\r
+\r
+  def install_dir_man(rel)\r
+    install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644\r
+  end\r
+\r
+  def install_files(list, dest, mode)\r
+    mkdir_p dest, @config.install_prefix\r
+    list.each do |fname|\r
+      install fname, dest, mode, @config.install_prefix\r
+    end\r
+  end\r
+\r
+  def libfiles\r
+    glob_reject(%w(*.y *.output), targetfiles())\r
+  end\r
+\r
+  def rubyextentions(dir)\r
+    ents = glob_select("*.#{@config.dllext}", targetfiles())\r
+    if ents.empty?\r
+      setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"\r
+    end\r
+    ents\r
+  end\r
+\r
+  def targetfiles\r
+    mapdir(existfiles() - hookfiles())\r
+  end\r
+\r
+  def mapdir(ents)\r
+    ents.map {|ent|\r
+      if File.exist?(ent)\r
+      then ent                         # objdir\r
+      else "#{curr_srcdir()}/#{ent}"   # srcdir\r
+      end\r
+    }\r
+  end\r
+\r
+  # picked up many entries from cvs-1.11.1/src/ignore.c\r
+  JUNK_FILES = %w( \r
+    core RCSLOG tags TAGS .make.state\r
+    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb\r
+    *~ *.old *.bak *.BAK *.orig *.rej _$* *$\r
+\r
+    *.org *.in .*\r
+  )\r
+\r
+  def existfiles\r
+    glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))\r
+  end\r
+\r
+  def hookfiles\r
+    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|\r
+      %w( config setup install clean ).map {|t| sprintf(fmt, t) }\r
+    }.flatten\r
+  end\r
+\r
+  def glob_select(pat, ents)\r
+    re = globs2re([pat])\r
+    ents.select {|ent| re =~ ent }\r
+  end\r
+\r
+  def glob_reject(pats, ents)\r
+    re = globs2re(pats)\r
+    ents.reject {|ent| re =~ ent }\r
+  end\r
+\r
+  GLOB2REGEX = {\r
+    '.' => '\.',\r
+    '$' => '\$',\r
+    '#' => '\#',\r
+    '*' => '.*'\r
+  }\r
+\r
+  def globs2re(pats)\r
+    /\A(?:#{\r
+      pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')\r
+    })\z/\r
+  end\r
+\r
+  #\r
+  # TASK test\r
+  #\r
+\r
+  TESTDIR = 'test'\r
+\r
+  def exec_test\r
+    unless File.directory?('test')\r
+      $stderr.puts 'no test in this package' if verbose?\r
+      return\r
+    end\r
+    $stderr.puts 'Running tests...' if verbose?\r
+    begin\r
+      require 'test/unit'\r
+    rescue LoadError\r
+      setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'\r
+    end\r
+    runner = Test::Unit::AutoRunner.new(true)\r
+    runner.to_run << TESTDIR\r
+    runner.run\r
+  end\r
+\r
+  #\r
+  # TASK clean\r
+  #\r
+\r
+  def exec_clean\r
+    exec_task_traverse 'clean'\r
+    rm_f @config.savefile\r
+    rm_f 'InstalledFiles'\r
+  end\r
+\r
+  alias clean_dir_bin noop\r
+  alias clean_dir_lib noop\r
+  alias clean_dir_data noop\r
+  alias clean_dir_conf noop\r
+  alias clean_dir_man noop\r
+\r
+  def clean_dir_ext(rel)\r
+    return unless extdir?(curr_srcdir())\r
+    make 'clean' if File.file?('Makefile')\r
+  end\r
+\r
+  #\r
+  # TASK distclean\r
+  #\r
+\r
+  def exec_distclean\r
+    exec_task_traverse 'distclean'\r
+    rm_f @config.savefile\r
+    rm_f 'InstalledFiles'\r
+  end\r
+\r
+  alias distclean_dir_bin noop\r
+  alias distclean_dir_lib noop\r
+\r
+  def distclean_dir_ext(rel)\r
+    return unless extdir?(curr_srcdir())\r
+    make 'distclean' if File.file?('Makefile')\r
+  end\r
+\r
+  alias distclean_dir_data noop\r
+  alias distclean_dir_conf noop\r
+  alias distclean_dir_man noop\r
+\r
+  #\r
+  # Traversing\r
+  #\r
+\r
+  def exec_task_traverse(task)\r
+    run_hook "pre-#{task}"\r
+    FILETYPES.each do |type|\r
+      if type == 'ext' and config('without-ext') == 'yes'\r
+        $stderr.puts 'skipping ext/* by user option' if verbose?\r
+        next\r
+      end\r
+      traverse task, type, "#{task}_dir_#{type}"\r
+    end\r
+    run_hook "post-#{task}"\r
+  end\r
+\r
+  def traverse(task, rel, mid)\r
+    dive_into(rel) {\r
+      run_hook "pre-#{task}"\r
+      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')\r
+      directories_of(curr_srcdir()).each do |d|\r
+        traverse task, "#{rel}/#{d}", mid\r
+      end\r
+      run_hook "post-#{task}"\r
+    }\r
+  end\r
+\r
+  def dive_into(rel)\r
+    return unless File.dir?("#{@srcdir}/#{rel}")\r
+\r
+    dir = File.basename(rel)\r
+    Dir.mkdir dir unless File.dir?(dir)\r
+    prevdir = Dir.pwd\r
+    Dir.chdir dir\r
+    $stderr.puts '---> ' + rel if verbose?\r
+    @currdir = rel\r
+    yield\r
+    Dir.chdir prevdir\r
+    $stderr.puts '<--- ' + rel if verbose?\r
+    @currdir = File.dirname(rel)\r
+  end\r
+\r
+  def run_hook(id)\r
+    path = [ "#{curr_srcdir()}/#{id}",\r
+             "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }\r
+    return unless path\r
+    begin\r
+      instance_eval File.read(path), path, 1\r
+    rescue\r
+      raise if $DEBUG\r
+      setup_rb_error "hook #{path} failed:\n" + $!.message\r
+    end\r
+  end\r
+\r
+end   # class Installer\r
+\r
+\r
+class SetupError < StandardError; end\r
+\r
+def setup_rb_error(msg)\r
+  raise SetupError, msg\r
+end\r
+\r
+if $0 == __FILE__\r
+  begin\r
+    ToplevelInstaller.invoke\r
+  rescue SetupError\r
+    raise if $DEBUG\r
+    $stderr.puts $!.message\r
+    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."\r
+    exit 1\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tasks/cpp.rake_example b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tasks/cpp.rake_example
new file mode 100644 (file)
index 0000000..c1a895a
--- /dev/null
@@ -0,0 +1,77 @@
+# EventMachine C++ Rakefile Stab Case
+# TODO : track header files as a build dependency...
+# TODO : cross platform support
+# TODO : configure style functionality
+namespace :cpp do
+
+  require 'rake/clean'
+
+  # *nix only atm...
+  module Cpp
+    class <<self
+      def cpp; "g++"; end
+      def archive; "ar"; end
+      def compile file, output, includes=nil, flags=nil
+        sh %{#{cpp} #{file} #{includes} #{flags} -c -o #{output}}
+      end
+      def link file, output, libs=nil, flags=nil
+        sh %{#{cpp} #{file} #{libs} #{flags} -o #{output}}
+      end
+      def static output, files
+        sh %{#{archive} cr #{output} #{files}}
+      end
+    end
+  end
+
+  module EmConfig
+    Path = ENV['EVENTMACHINE_SOURCE'] || 'ext'
+    Sources = FileList["#{Path}/*.cpp"]
+    Sources.delete_if { |s| /ruby/ =~ s }
+    Compiled = Sources.sub(%r{^#{Path}/(.*)\.cpp}, "#{Path}/\\1.o")
+
+    Flags = "-O2 -pipe -fno-common -DOS_UNIX -DWITHOUT_SSL"
+    Includes = ""
+    Libs = ''
+  end
+  CLEAN.include(EmConfig::Compiled)
+
+  rule %r{^#{EmConfig::Path}/.*\.o$} => [proc { |targ| 
+    targ.sub(%r{^#{EmConfig::Path}/(.*)\.o$}, "#{EmConfig::Path}/\\1.cpp")
+    }] do |t|
+    Cpp.compile t.source, t.name, EmConfig::Includes, EmConfig::Flags
+  end
+
+  file "#{EmConfig::Path}/libeventmachine.a" => EmConfig::Compiled do |t|
+    Cpp.static t.name, EmConfig::Compiled
+  end
+  CLEAN.include("#{EmConfig::Path}/libeventmachine.a")
+
+  module AppConfig
+    Appname = 'echo_em'
+    Sources = FileList['*.cpp']
+    Compiled = Sources.sub(%r{^(.*)\.cpp}, '\\1.o')
+
+    Flags = ["", EmConfig::Flags].join(' ')
+    Includes = ["-I. -I#{EmConfig::Path}", EmConfig::Includes].join(' ')
+    Libs = ["-L#{EmConfig::Path} -leventmachine", EmConfig::Libs].join(' ')
+  end
+  CLEAN.include(AppConfig::Compiled)
+  CLEAN.include(AppConfig::Appname)
+
+  rule %r{^.*\.o$} => [proc { |targ| 
+    targ.sub(%r{^(.*)\.o$}, '\\1.cpp')
+    }] do |t|
+    Cpp.compile t.source, t.name, AppConfig::Includes, AppConfig::Flags
+  end
+
+  file AppConfig::Appname => ["#{EmConfig::Path}/libeventmachine.a", AppConfig::Compiled] do |t|
+    Cpp.link AppConfig::Compiled, t.name, AppConfig::Libs, AppConfig::Flags
+  end
+
+  task :build => AppConfig::Appname
+
+  task :run => AppConfig::Appname do
+    sh "./#{AppConfig::Appname}"
+  end
+
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.crt b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.crt
new file mode 100644 (file)
index 0000000..da886b6
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----\r
+MIIFRDCCAywCAQEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCRU0xFTATBgNV\r
+BAgTDEV2ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQL\r
+EwtEZXZlbG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMB4XDTA5MDMyOTAy\r
+MzE0NloXDTEwMDMyOTAyMzE0NlowaDELMAkGA1UEBhMCRU0xFTATBgNVBAgTDEV2\r
+ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQLEwtEZXZl\r
+bG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMIICIjANBgkqhkiG9w0BAQEF\r
+AAOCAg8AMIICCgKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b\r
++hkrp9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cK\r
+OwzxCFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77l\r
+S8n2AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p\r
+67baDHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXg\r
+C8C8cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1N\r
+uzWD81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsy\r
+XXEZ2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+\r
+B69FJRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxD\r
+ohhAoKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgK\r
+iQYEnb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsC\r
+AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAj7J8fy1LUWoVWnrXDAC9jwJ1nI/YjoSU\r
+6ywke3o04+nZC5S+dPnuVy+HAwsU940CoNvP6RStI/bH6JL+NIqEFmwM3M8xIEWV\r
+MYVPkfvQUxxGvDnaY7vv93u+6Q77HV3qlhAQBHChyuXyO7TG3+WzsiT9AnBNtAP0\r
+4jClt5kCAQXLO/p0SFEZQ8Ru9SM8d1i73Z0VDVzs8jYWlBhiherSgbw1xK4wBOpJ\r
+43XmjZsBSrDpiAXd07Ak3UL2GjfT7eStgebL3UIe39ThE/s/+l43bh0M6WbOBvyQ\r
+i/rZ50kd1GvN0xnZhtv07hIJWO85FGWi7Oet8AzdUZJ17v1Md/f2vdhPVTFN9q+w\r
+mQ6LxjackqCvaJaQfBEbqsn2Tklxk4tZuDioiQbOElT2e6vljQVJWIfNx38Ny2LM\r
+aiXQPQu+4CI7meAh5gXM5nyJGbZvRPsxj89CqYzyHCYs5HBP3AsviBvn26ziOF+c\r
+544VmHd9HkIv8UTC29hh+R64RlgMQQQdaXFaUrFPTs/do0k8n/c2bPc0iTdfi5Q2\r
+gq6Vi8q6Ay5wGgTtRRbn/mWKuCFjEh94z6pF9Xr06NX0PuEOdf+Ls9vI5vz6G0w6\r
+0Li7devEN7EKBY+7Mcjg918yq9i5tEiMkUgT68788t3fTC+4iUQ5fDtdrHsaOlIR\r
+8bs/XQVNE/s=\r
+-----END CERTIFICATE-----\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.key b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/client.key
new file mode 100644 (file)
index 0000000..306dc33
--- /dev/null
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----\r
+MIIJKAIBAAKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b+hkr\r
+p9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cKOwzx\r
+CFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77lS8n2\r
+AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p67ba\r
+DHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXgC8C8\r
+cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1NuzWD\r
+81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsyXXEZ\r
+2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+B69F\r
+JRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxDohhA\r
+oKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgKiQYE\r
+nb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsCAwEA\r
+AQKCAgB495RDRQB9x6hX3F+DviI8rDGug+h5FAiwJ0IBG2o1kNdbNVsTC5dvpEmg\r
+uPHaugCaEP+PMZbU34mNklKlb+7QbPbH18UGqz5so9TlmYOXz9oaKD6nAWL9nqRo\r
+02pCXQDR3DuxbhbgFnFTIECJ/jqXkl2toGaVp83W+6kZkHP8srkMyLASihWgosc+\r
+xRWAGvaAZtNz7br+eT5fxuH/SEKPOl1qAZ23kXrXm1XQfizk8MnMTptkUMYv+hfl\r
+TM98BASUsiTs6g+opy43HFn09naOQcqkWZO/8s6Gbvhi2lVfZqi5Ba6g3lVYJ3gU\r
+kGoako4N9qB7WqJz+LYjVR9C4TbkkJ9OD6ArwGAx5IIzC3XKSxCyY/pUn4YumPhY\r
+fjvY/km54TBtx/isS1TAgjSgDUxbzrfbkh7afOXSOniy9bWJMgNqHF61dqxWxmUg\r
+F5Tch9zH3qFFVkXpYzDU/R8ZV+CRouCvhn0eZYDh8IqIAwjH0VjkxjPyQtrdrMd3\r
+gDKMVKoY31EOMLZzv8a0prjpr15A+uw30tT336qb3fofks4pZKUJw8ru9jJVir2p\r
++RML6iUHCmIeceF7/N1meooSMLPJe0xgKeMb9M4Wtd/et2UNVtP8nCDG622rf2a0\r
+F/EudXuFgc3FB8nXRw9TCkw9xKQff38edG5xPFUEgqObbVl5YQKCAQEA5DDKGOmp\r
+EO5Zuf/kZfG6/AMMYwAuv1HrYTV2w/HnI3tyQ34Xkeqo+I/OqmRk68Ztxw4Kx1So\r
+SRavkotrlWhhDpl2+Yn1BjkHktSoOdf9gJ9z9llkLmbOkBjmupig1NUB7fq/4y2k\r
+MdqJXDy3uVKHJ97gxdIheMTyHiKuMJPnuT5lZtlT210Ig82P7sLQb/sgCfKVFTr0\r
+Z3haQ5/tBNKjq+igT4nMBWupOTD1q2GeZLIZACnmnUIhvu+3/bm0l+wiCB0DqF0T\r
+Wy9tlL3fqQSCqzevL7/k5Lg6tJTaP/XYePB73TsOtAXgIaoltXgRBsBUeE1eaODx\r
+kMT6E1PPtn7EqQKCAQEA1qImmTWGqhKICrwje40awPufFtZ/qXKVCN/V+zYsrJV1\r
+EnZpUDM+zfitlQCugnrQVHSpgfekI6mmVkmogO3fkNjUFTq+neg7IHOUHnqotx+3\r
+NMqIsyFInGstu9mfPd26fzZjUtx5wKF38LDTIJJAEJ83U3UpPBfpwKmiOGDXOa54\r
+2i4em/bb/hrQR6JySruZYLi0fXnGI5ZOfpkHgC/KOFkKNKAg2oh4B9qo7ACyiSNk\r
+yojb2mmn6g1OLPxi7wGUSrkS1HQq4an6RZ+eUO0HXVWag0QStdQ91M9IrIHgSBBG\r
+0e86Ar6jtD579gqsbz4ySpI/FqEI9obTC+E1/b0aIwKCAQAGz334qGCnZLXA22ZR\r
+tJlEFEM2YTcD9snzqMjWqE2hvXl3kjfZ3wsUABbG9yAb+VwlaMHhmSE8rTSoRwj6\r
++JaM/P+UCw4JFYKoWzh6IXwrbpbjb1+SEvdvTY71WsDSGVlpZOZ9PUt9QWyAGD/T\r
+hCcMhZZn0RG2rQoc5CQWxxNPcBFOtIXQMkKizGvTUHUwImqeYWMZsxzASdNH2WoV\r
+jsPbyaGfPhmcv83ZKyDp8IvtrXMZkiaT4vlm3Xi8VeKR9jY9z7/gMob1XcEDg3c9\r
+cCkGOy87WZrXSLhX02mAJzJCycqom66gqNw7pPxjIiY/8VWUEZsTvkL3cymTkhjM\r
+9ZOhAoIBAGUaNqJe01NTrV+ZJgGyAxM6s8LXQYV5IvjuL2bJKxwUvvP2cT9FFGWD\r
+qYiRrKJr5ayS07IUC+58oIzu33/0DSa27JgfduD9HrT3nKMK1mSEfRFSAjiXChQc\r
+bIubRGapBoub/AdxMazqoovvT1R9b84kobQfcVAMV6DYh0CVZWyXYfgsV2DSVOiK\r
+iufjfoDzg5lLCEI+1XW3/LunrB/W4yPN1X/amf8234ublYyt+2ucD4NUGnP05xLa\r
+N6P7M0MwdEEKkvMe0YBBSFH5kWK/dIOjqkgBDes20fVnuuz/tL1dZW7IiIP4dzaV\r
+ZGEOwBEatCfqYetv6b/u3IUxDfS7Wg8CggEBALoOwkn5LGdQg+bpdZAKJspGnJWL\r
+Kyr9Al2tvgc69rxfpZqS5eDLkYYCzWPpspSt0Axm1O7xOUDQDt42luaLNGJzHZ2Q\r
+Hn0ZNMhyHpe8d8mIQngRjD+nuLI/uFUglPzabDOCOln2aycjg1mA6ecXP1XMEVbu\r
+0RB/0IE36XTMfZ+u9+TRjkBLpmUaX1FdIQQWfwUou/LfaXotoQlhSGAcprLrncuJ\r
+T44UATYEgO/q9pMM33bdE3eBYZHoT9mSvqoLCN4s0LuwOYItIxLKUj0GulL0VQOI\r
+SZi+0A1c8cVDXgApkBrWPDQIR9JS4de0gW4hnDoUvHtUc2TYPRnz6N9MtFY=\r
+-----END RSA PRIVATE KEY-----\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_attach.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_attach.rb
new file mode 100644 (file)
index 0000000..6b46bbc
--- /dev/null
@@ -0,0 +1,126 @@
+# $Id$
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+
+$:.unshift "../lib"
+require 'eventmachine'
+require 'socket'
+require 'test/unit'
+
+
+class TestAttach < Test::Unit::TestCase
+
+  Host = "127.0.0.1"
+  Port = 9550
+
+  class EchoServer < EM::Connection
+    def receive_data data
+      send_data data
+    end
+  end
+
+  class EchoClient < EM::Connection
+    def initialize
+      self.notify_readable = true
+      $sock.write("abc\n")
+    end
+
+    def notify_readable
+      $read = $sock.readline
+      $fd = detach
+    end
+
+    def unbind
+      EM.next_tick do
+        $sock.write("def\n")
+        EM.add_timer(0.5){ EM.stop }
+      end
+    end
+  end
+
+  def test_attach
+    EM.run{
+      EM.start_server Host, Port, EchoServer
+      $sock = TCPSocket.new Host, Port
+      EM.watch $sock, EchoClient
+    }
+
+    assert_equal $read, "abc\n"
+    unless defined? JRuby # jruby filenos are not real
+      assert_equal $fd, $sock.fileno
+    end
+    assert_equal false, $sock.closed?
+    assert_equal $sock.readline, "def\n"
+  end
+
+  module PipeWatch
+    def notify_readable
+      $read = $r.readline
+      EM.stop
+    end
+  end
+
+  def test_attach_pipe
+    EM.run{
+      $r, $w = IO.pipe
+      EM.watch $r, PipeWatch do |c|
+        c.notify_readable = true
+      end
+      $w.write("ghi\n")
+    }
+
+    assert_equal $read, "ghi\n"
+  end
+
+  def test_set_readable
+    EM.run{
+      $r, $w = IO.pipe
+      c = EM.watch $r, PipeWatch do |c|
+        c.notify_readable = false
+      end
+
+      EM.next_tick{
+        $before = c.notify_readable?
+        c.notify_readable = true
+        $after = c.notify_readable?
+      }
+
+      $w.write("jkl\n")
+    }
+
+    assert !$before
+    assert $after
+    assert_equal $read, "jkl\n"
+  end
+
+  module PipeReader
+    def receive_data data
+      $read = data
+      EM.stop
+    end
+  end
+
+  def test_read_write_pipe
+    EM.run{
+      $r, $w = IO.pipe
+      EM.attach $r, PipeReader
+      writer = EM.attach($w)
+      writer.send_data 'ghi'
+    }
+
+    assert_equal $read, "ghi"
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_basic.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_basic.rb
new file mode 100644 (file)
index 0000000..b867c40
--- /dev/null
@@ -0,0 +1,284 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+# 
+
+$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
+require 'eventmachine'
+require 'socket'
+require 'test/unit'
+
+class TestBasic < Test::Unit::TestCase
+
+  def setup
+    assert(!EM.reactor_running?)
+  end
+
+  def teardown
+    assert(!EM.reactor_running?)
+  end
+
+  #-------------------------------------
+
+  def test_libtype
+    lt = EventMachine.library_type
+    em_lib = (ENV["EVENTMACHINE_LIBRARY"] || $eventmachine_library || :xxx).to_sym
+
+    # Running from test runner, under jruby.
+    if RUBY_PLATFORM == 'java'
+      unless em_lib == :pure_ruby
+        assert_equal( :java, lt )
+        return
+      end
+    end
+
+    case em_lib
+    when :pure_ruby
+      assert_equal( :pure_ruby, lt )
+    when :extension
+      assert_equal( :extension, lt )
+    when :java
+      assert_equal( :java, lt )
+    else
+      # Running from jruby as a standalone test.
+      if RUBY_PLATFORM == 'java'
+        assert_equal( :java, lt )
+      else
+        assert_equal( :extension, lt )
+      end
+    end
+  end
+
+  #-------------------------------------
+
+
+  def test_em
+    EventMachine.run {
+      EventMachine.add_timer 0 do
+        EventMachine.stop
+      end
+    }
+  end
+
+  #-------------------------------------
+
+  def test_timer
+    n = 0
+    EventMachine.run {
+      EventMachine.add_periodic_timer(0.1) {
+        n += 1
+        EventMachine.stop if n == 2
+      }
+    }
+  end
+
+  #-------------------------------------
+
+  # This test once threw an already-running exception.
+  module Trivial
+    def post_init
+      EventMachine.stop
+    end
+  end
+
+  def test_server
+    EventMachine.run {
+      EventMachine.start_server "localhost", 9000, Trivial
+      EventMachine.connect "localhost", 9000
+    }
+    assert( true ) # make sure it halts
+  end
+
+  #--------------------------------------
+
+  # EventMachine#run_block starts the reactor loop, runs the supplied block, and then STOPS
+  # the loop automatically. Contrast with EventMachine#run, which keeps running the reactor
+  # even after the supplied block completes.
+  def test_run_block
+    assert !EM.reactor_running?
+    a = nil
+    EM.run_block { a = "Worked" }
+    assert a
+    assert !EM.reactor_running?
+  end
+
+
+  #--------------------------------------
+
+  # TODO! This is an unfinished edge case.
+  # EM mishandles uncaught Ruby exceptions that fire from within #unbind handlers.
+  # A uncaught Ruby exception results in a call to EM::release_machine (which is in an ensure
+  # block in EM::run). But if EM is processing an unbind request, the release_machine call
+  # will cause a segmentation fault.
+  #
+
+  TestHost = "127.0.0.1"
+  TestPort = 9070
+
+  class UnbindError < EM::Connection
+    def initialize *args
+      super
+    end
+    def connection_completed
+      close_connection_after_writing
+    end
+    def unbind
+      raise "Blooey"
+    end
+  end
+
+  def xxx_test_unbind_error
+    assert_raises( RuntimeError ) {
+      EM.run {
+        EM.start_server TestHost, TestPort
+        EM.connect TestHost, TestPort, UnbindError
+      }
+    }
+  end
+
+  #------------------------------------
+  #
+  # TODO. This is an unfinished bug fix.
+  # This case was originally reported by Dan Aquino. If you throw a Ruby exception
+  # in a post_init handler, it gets rethrown as a confusing reactor exception.
+  # The problem is in eventmachine.rb, which calls post_init within the private
+  # initialize method of the EM::Connection class. This happens in both the EM::connect
+  # method and in the code that responds to connection-accepted events.
+  # What happens is that we instantiate the new connection object, which calls
+  # initialize, and then after initialize returns, we stick the new connection object
+  # into EM's @conns hashtable.
+  # But the problem is that Connection::initialize calls #post_init before it returns,
+  # and this may be user-written code that may throw an uncaught Ruby exception.
+  # If that happens, the reactor will abort, and it will then try to run down open
+  # connections. Because @conns never got a chance to properly reflect the new connection
+  # (because initialize never returned), we throw a ConnectionNotBound error
+  # (eventmachine.rb line 1080).
+  # When the bug is fixed, activate this test case.
+  #
+
+  class PostInitError < EM::Connection
+    def post_init
+      aaa bbb # should produce a Ruby exception
+    end
+  end
+  # This test causes issues, the machine becomes unreleasable after 
+  # release_machine suffers an exception in event_callback.
+  def xxx_test_post_init_error
+    assert_raises( EventMachine::ConnectionNotBound ) {
+      EM.run {
+        EM::Timer.new(1) {EM.stop}
+        EM.start_server TestHost, TestPort
+        EM.connect TestHost, TestPort, PostInitError
+      }
+    }
+    EM.run {
+      EM.stop
+    }
+    assert !EM.reactor_running?
+  end
+
+  module BrsTestSrv
+    def receive_data data
+      $received << data
+    end
+    def unbind
+      EM.stop
+    end
+  end
+  module BrsTestCli
+    def post_init
+      send_data $sent
+      close_connection_after_writing
+    end
+  end
+
+  # From ticket #50
+  def test_byte_range_send
+    $received = ''
+    $sent = (0..255).to_a.pack('C*')
+    EM::run {
+      EM::start_server TestHost, TestPort, BrsTestSrv
+      EM::connect TestHost, TestPort, BrsTestCli
+
+      EM::add_timer(0.5) { assert(false, 'test timed out'); EM.stop; Kernel.warn "test timed out!" }
+    }
+    assert_equal($sent, $received)
+  end
+
+  def test_bind_connect
+    local_ip = UDPSocket.open {|s| s.connect('google.com', 80); s.addr.last }
+
+    bind_port = rand(33333)+1025
+
+    test = self
+    EM.run do
+      EM.start_server(TestHost, TestPort, Module.new do
+        define_method :post_init do
+          begin
+            test.assert_equal bind_port, Socket.unpack_sockaddr_in(get_peername).first
+            test.assert_equal local_ip, Socket.unpack_sockaddr_in(get_peername).last
+          ensure
+            EM.stop_event_loop
+          end
+        end
+      end)
+      EM.bind_connect local_ip, bind_port, TestHost, TestPort
+    end
+  end
+
+  def test_reactor_thread?
+    assert !EM.reactor_thread?
+    EM.run { assert EM.reactor_thread?; EM.stop }
+    assert !EM.reactor_thread?
+  end
+
+  def test_schedule_on_reactor_thread
+    x = false
+    EM.run do
+      EM.schedule { x = true }
+      EM.stop
+    end
+    assert x
+  end
+  
+  def test_schedule_from_thread
+    x = false
+    assert !x
+    EM.run do
+      Thread.new { EM.schedule { x = true; EM.stop } }.join
+    end
+    assert x
+  end
+
+  def test_set_heartbeat_interval
+    interval = 0.5
+    EM.run {
+      EM.set_heartbeat_interval interval
+      $interval = EM.get_heartbeat_interval
+      EM.stop
+    }
+    assert_equal(interval, $interval)
+  end
+end
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_channel.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_channel.rb
new file mode 100644 (file)
index 0000000..dd2d124
--- /dev/null
@@ -0,0 +1,63 @@
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestEventMachineChannel < Test::Unit::TestCase
+  def test_channel_subscribe
+    s = 0
+    EM.run do
+      c = EM::Channel.new
+      c.subscribe { |v| s = v; EM.stop }
+      c << 1
+    end
+    assert_equal 1, s
+  end
+
+  def test_channel_unsubscribe
+    s = 0
+    EM.run do
+      c = EM::Channel.new
+      subscription = c.subscribe { |v| s = v }
+      c.unsubscribe(subscription)
+      c << 1
+      EM.next_tick { EM.stop }
+    end
+    assert_not_equal 1, s
+  end
+
+  def test_channel_pop
+    s = 0
+    EM.run do
+      c = EM::Channel.new
+      c.pop{ |v| s = v }
+      c << 1
+      c << 2
+      EM.next_tick { EM.stop }
+    end
+    assert_equal 1, s
+  end
+
+  def test_channel_reactor_thread_push
+    out = []
+    c = EM::Channel.new
+    c.subscribe { |v| out << v }
+    Thread.new { c.push(1,2,3) }.join
+    assert out.empty?
+
+    EM.run { EM.next_tick { EM.stop } }
+
+    assert_equal [1,2,3], out
+  end
+
+  def test_channel_reactor_thread_callback
+    out = []
+    c = EM::Channel.new
+    Thread.new { c.subscribe { |v| out << v } }.join
+    c.push(1,2,3)
+    assert out.empty?
+
+    EM.run { EM.next_tick { EM.stop } }
+
+    assert_equal [1,2,3], out
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_connection_count.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_connection_count.rb
new file mode 100644 (file)
index 0000000..ce13205
--- /dev/null
@@ -0,0 +1,35 @@
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestConnectionCount < Test::Unit::TestCase
+  def test_idle_connection_count
+    EM.run {
+      $count = EM.connection_count
+      EM.stop_event_loop
+    }
+
+    assert_equal(0, $count)
+  end
+
+  module Client
+    def connection_completed
+      $client_conns += 1
+      EM.stop if $client_conns == 3
+    end
+  end
+
+  def test_with_some_connections
+    EM.run {
+      $client_conns = 0
+      $initial_conns = EM.connection_count
+      EM.start_server("127.0.0.1", 9999)
+      $server_conns = EM.connection_count
+      3.times { EM.connect("127.0.0.1", 9999, Client) }
+    }
+
+    assert_equal(0, $initial_conns)
+    assert_equal(1, $server_conns)
+    assert_equal(4, $client_conns + $server_conns)
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_defer.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_defer.rb
new file mode 100644 (file)
index 0000000..9a432db
--- /dev/null
@@ -0,0 +1,47 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestDeferUsage < Test::Unit::TestCase\r
+\r
+  def test_defers\r
+    n = 0\r
+    n_times = 20\r
+    EM.run {\r
+      n_times.times {\r
+        work_proc = proc { n += 1 }\r
+        callback = proc { EM.stop if n == n_times }\r
+        EM.defer work_proc, callback\r
+      }\r
+    }\r
+    assert_equal( n, n_times )\r
+  end unless RUBY_VERSION >= '1.9.0'\r
+\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_epoll.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_epoll.rb
new file mode 100644 (file)
index 0000000..1c9aae1
--- /dev/null
@@ -0,0 +1,160 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+# TODO, and I know this doesn't belong here, but if a datagram calls
+# send_data outside of a receive_data, there is no return address, and
+# the result is a very confusing error message.
+#
+
+require 'eventmachine'
+require 'test/unit'
+
+
+class TestEpoll < Test::Unit::TestCase
+
+  module TestEchoServer
+    def receive_data data
+      send_data data
+      close_connection_after_writing
+    end
+  end
+
+  module TestEchoClient
+    def connection_completed
+      send_data "ABCDE"
+      $max += 1
+    end
+    def receive_data data
+      raise "bad response" unless data == "ABCDE"
+    end
+    def unbind
+      $n -= 1
+      EM.stop if $n == 0
+    end
+  end
+
+
+  # We can set the rlimit/nofile of a process but we can only set it
+  # higher if we're running as root.
+  # On most systems, the default value is 1024.
+  # Java doesn't (currently) implement this.
+  def test_rlimit
+    unless RUBY_PLATFORM =~ /java/ or EM.set_descriptor_table_size >= 1024
+      a = EM.set_descriptor_table_size
+      assert( a <= 1024 )
+      a = EM.set_descriptor_table_size( 1024 )
+      assert( a == 1024 )
+    end
+  end
+
+  # Run a high-volume version of this test by kicking the number of connections
+  # up past 512. (Each connection uses two sockets, a client and a server.)
+  # (Will require running the test as root)
+  # This test exercises TCP clients and servers.
+  #
+  # XXX this test causes all sort of weird issues on OSX (when run as part of the suite)
+  def _test_descriptors
+    EM.epoll
+    s = EM.set_descriptor_table_size 60000
+    EM.run {
+      EM.start_server "127.0.0.1", 9800, TestEchoServer
+      $n = 0
+      $max = 0
+      100.times {
+        EM.connect("127.0.0.1", 9800, TestEchoClient) {$n += 1}
+      }
+    }
+    assert_equal(0, $n)
+    assert_equal(100, $max)
+  end
+
+  def test_defer
+    n = 0
+    work_proc = proc {n += 1}
+    callback_proc = proc {EM.stop}
+    EM.run {
+      EM.defer work_proc, callback_proc
+    }
+    assert_equal( 1, n )
+  end unless RUBY_VERSION >= '1.9.0'
+
+
+  module TestDatagramServer
+    def receive_data dgm
+      $in = dgm
+      send_data "abcdefghij"
+    end
+  end
+  module TestDatagramClient
+    def post_init
+      send_datagram "1234567890", "127.0.0.1", 9500
+    end
+    def receive_data dgm
+      $out = dgm
+      EM.stop
+    end
+  end
+
+  def test_datagrams
+    $in = $out = ""
+    EM.run {
+      EM.open_datagram_socket "127.0.0.1", 9500, TestDatagramServer
+      EM.open_datagram_socket "127.0.0.1", 0, TestDatagramClient
+    }
+    assert_equal( "1234567890", $in )
+    assert_equal( "abcdefghij", $out )
+  end
+
+  # XXX this test fails randomly..
+  def _test_unix_domain
+    fn = "/tmp/xxx.chain"
+    EM.epoll
+    s = EM.set_descriptor_table_size 60000
+    EM.run {
+      # The pure-Ruby version won't let us open the socket if the node already exists.
+      # Not sure, that actually may be correct and the compiled version is wrong.
+      # Pure Ruby also oddly won't let us make that many connections. This test used
+      # to run 100 times. Not sure where that lower connection-limit is coming from in
+      # pure Ruby.
+      # Let's not sweat the Unix-ness of the filename, since this test can't possibly
+      # work on Windows anyway.
+      #
+      File.unlink(fn) if File.exist?(fn)
+      EM.start_unix_domain_server fn, TestEchoServer
+      $n = 0
+      $max = 0
+      50.times {
+        EM.connect_unix_domain(fn, TestEchoClient) {$n += 1}
+      }
+      EM::add_timer(1) { $stderr.puts("test_unix_domain timed out!"); EM::stop }
+    }
+    assert_equal(0, $n)
+    assert_equal(50, $max)
+  ensure
+    File.unlink(fn) if File.exist?(fn)
+  end
+
+end
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_error_handler.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_error_handler.rb
new file mode 100644 (file)
index 0000000..214e67e
--- /dev/null
@@ -0,0 +1,35 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestErrorHandler < Test::Unit::TestCase\r
+  def test_error_handler\r
+    error = nil\r
+    EM.error_handler{ |e|\r
+      error = e\r
+      EM.error_handler(nil)\r
+      EM.stop\r
+    }\r
+\r
+    assert_nothing_raised do\r
+      EM.run{\r
+        EM.add_timer(0){\r
+          raise 'test'\r
+        }\r
+      }\r
+    end\r
+\r
+    assert_equal error.class, RuntimeError\r
+    assert_equal error.message, 'test'\r
+  end\r
+\r
+  def test_without_error_handler\r
+    assert_raise RuntimeError do\r
+      EM.run{\r
+        EM.add_timer(0){\r
+          raise 'test'\r
+        }\r
+      }\r
+    end\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_errors.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_errors.rb
new file mode 100644 (file)
index 0000000..3a890fa
--- /dev/null
@@ -0,0 +1,82 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+\r
+###### THIS TEST IS NOW OBSOLETE.\r
+###### As of 27Dec07, the hookable error handling is obsolete because\r
+###### of its performance impact.\r
+\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestErrors < Test::Unit::TestCase\r
+\r
+  Localhost = "127.0.0.1"\r
+  Localport = 9801\r
+\r
+  def setup\r
+  end\r
+\r
+  def obsolete_teardown\r
+    # Calling #set_runtime_error_hook with no block restores the\r
+    # default handling of runtime_errors.\r
+    #\r
+    EM.set_runtime_error_hook\r
+  end\r
+\r
+  def test_no_tests_stub\r
+  end\r
+\r
+  # EM has a default handler for RuntimeErrors that are emitted from\r
+  # user written code. You can override the handler if you wish, but it's\r
+  # easier to call #set_runtime_error_hook.\r
+  # Ordinarily, an error in user code invoked by the reactor aborts the\r
+  # run.\r
+  #\r
+  def obsolete_test_unhandled_error\r
+    assert_raises( RuntimeError ) {\r
+      EM.run {\r
+        EM.add_timer(0) {raise "AAA"}\r
+      }\r
+    }\r
+\r
+  end\r
+\r
+  def obsolete_test_handled_error\r
+    err = nil\r
+    EM.run {\r
+      EM.set_runtime_error_hook {\r
+        err = true\r
+        EM.stop\r
+      }\r
+      EM.add_timer(0) {raise "AAA"}\r
+    }\r
+    assert err\r
+  end\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_exc.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_exc.rb
new file mode 100644 (file)
index 0000000..19c36e7
--- /dev/null
@@ -0,0 +1,55 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestSomeExceptions < Test::Unit::TestCase\r
+\r
+  # Read the commentary in EventMachine#run.\r
+  # This test exercises the ensure block in #run that makes sure\r
+  # EventMachine#release_machine gets called even if an exception is\r
+  # thrown within the user code. Without the ensured call to release_machine,\r
+  # the second call to EventMachine#run will fail with a C++ exception\r
+  # because the machine wasn't cleaned up properly.\r
+\r
+  def test_a\r
+    assert_raises(RuntimeError) {\r
+      EventMachine.run {\r
+      raise "some exception"\r
+    }\r
+    }\r
+  end\r
+\r
+  def test_b\r
+    assert_raises(RuntimeError) {\r
+      EventMachine.run {\r
+      raise "some exception"\r
+    }\r
+    }\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_file_watch.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_file_watch.rb
new file mode 100644 (file)
index 0000000..841fd60
--- /dev/null
@@ -0,0 +1,49 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestFileWatch < Test::Unit::TestCase\r
+  module FileWatcher\r
+    def file_modified\r
+      $modified = true\r
+    end\r
+    def file_deleted\r
+      $deleted = true\r
+    end\r
+    def unbind\r
+      $unbind = true\r
+      EM.stop\r
+    end\r
+  end\r
+\r
+  def setup\r
+    EM.kqueue = true if EM.kqueue?\r
+  end\r
+\r
+  def teardown\r
+    EM.kqueue = false if EM.kqueue?\r
+  end\r
+\r
+  def test_events\r
+    EM.run{\r
+      require 'tempfile'\r
+      file = Tempfile.new('em-watch')\r
+      $tmp_path = file.path\r
+\r
+      # watch it\r
+      watch = EM.watch_file(file.path, FileWatcher)\r
+      $path = watch.path\r
+\r
+      # modify it\r
+      File.open(file.path, 'w'){ |f| f.puts 'hi' }\r
+\r
+      # delete it\r
+      EM.add_timer(0.25){ file.close; file.delete }\r
+    }\r
+\r
+    assert_equal($path, $tmp_path)\r
+    assert($modified)\r
+    assert($deleted)\r
+    assert($unbind)\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_futures.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_futures.rb
new file mode 100644 (file)
index 0000000..0a539b5
--- /dev/null
@@ -0,0 +1,198 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestFutures < Test::Unit::TestCase\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+  end\r
+\r
+  def test_future\r
+      assert_equal(100, EventMachine::Deferrable.future(100) )\r
+\r
+      p1 = proc { 100 + 1 }\r
+      assert_equal(101, EventMachine::Deferrable.future(p1) )\r
+  end\r
+\r
+  class MyFuture\r
+      include EventMachine::Deferrable\r
+      def initialize *args\r
+        super\r
+        set_deferred_status :succeeded, 40\r
+      end\r
+  end\r
+\r
+  class MyErrorFuture\r
+      include EventMachine::Deferrable\r
+      def initialize *args\r
+        super\r
+        set_deferred_status :failed, 41\r
+      end\r
+  end\r
+\r
+\r
+  def test_future_1\r
+      # Call future with one additional argument and it will be treated as a callback.\r
+      def my_future\r
+        MyFuture.new\r
+      end\r
+\r
+      value = nil\r
+      EventMachine::Deferrable.future my_future, proc {|v| value=v}\r
+      assert_equal( 40, value )\r
+  end\r
+\r
+\r
+  def test_future_2\r
+      # Call future with two additional arguments and they will be treated as a callback\r
+      # and an errback.\r
+      value = nil\r
+      EventMachine::Deferrable.future MyErrorFuture.new, nil, proc {|v| value=v}\r
+      assert_equal( 41, value )\r
+  end\r
+\r
+\r
+  def test_future_3\r
+      # Call future with no additional arguments but with a block, and the block will be\r
+      # treated as a callback.\r
+      value = nil\r
+      EventMachine::Deferrable.future MyFuture.new do |v|\r
+        value=v\r
+      end\r
+      assert_equal( 40, value )\r
+  end\r
+\r
+\r
+  class RecursiveCallback\r
+      include EventMachine::Deferrable\r
+  end\r
+\r
+  # A Deferrable callback can call #set_deferred_status to change the values\r
+  # passed to subsequent callbacks.\r
+  #\r
+  def test_recursive_callbacks\r
+      n = 0 # counter assures that all the tests actually run.\r
+      rc = RecursiveCallback.new\r
+      rc.callback {|a|\r
+        assert_equal(100, a)\r
+        n += 1\r
+        rc.set_deferred_status :succeeded, 101, 101\r
+      }\r
+      rc.callback {|a,b|\r
+        assert_equal(101, a)\r
+        assert_equal(101, b)\r
+        n += 1\r
+        rc.set_deferred_status :succeeded, 102, 102, 102\r
+      }\r
+      rc.callback {|a,b,c|\r
+        assert_equal(102, a)\r
+        assert_equal(102, b)\r
+        assert_equal(102, c)\r
+        n += 1\r
+      }\r
+      rc.set_deferred_status :succeeded, 100\r
+      assert_equal(3, n)\r
+  end\r
+\r
+  def test_syntactic_sugar\r
+    rc = RecursiveCallback.new\r
+    rc.set_deferred_success 100\r
+    rc.set_deferred_failure 200\r
+  end\r
+\r
+  # It doesn't raise an error to set deferred status more than once.\r
+  # In fact, this is a desired and useful idiom when it happens INSIDE\r
+  # a callback or errback.\r
+  # However, it's less useful otherwise, and in fact would generally be\r
+  # indicative of a programming error. However, we would like to be resistant\r
+  # to such errors. So whenever we set deferred status, we also clear BOTH\r
+  # stacks of handlers.\r
+  #\r
+  def test_double_calls\r
+    s = 0\r
+    e = 0\r
+\r
+    d = EM::DefaultDeferrable.new\r
+    d.callback {s += 1}\r
+    d.errback {e += 1}\r
+\r
+    d.succeed  # We expect the callback to be called, and the errback to be DISCARDED.\r
+    d.fail       # Presumably an error. We expect the errback NOT to be called.\r
+    d.succeed  # We expect the callback to have been discarded and NOT to be called again.\r
+\r
+    assert_equal(1, s)\r
+    assert_equal(0, e)\r
+  end\r
+\r
+  # Adding a callback to a Deferrable that is already in a success state executes the callback\r
+  # immediately. The same applies to a an errback added to an already-failed Deferrable.\r
+  # HOWEVER, we expect NOT to be able to add errbacks to succeeded Deferrables, or callbacks\r
+  # to failed ones.\r
+  #\r
+  # We illustrate this with a rather contrived test. The test calls #fail after #succeed,\r
+  # which ordinarily would not happen in a real program.\r
+  #\r
+  # What we're NOT attempting to specify is what happens if a Deferrable is succeeded and then\r
+  # failed (or vice-versa). Should we then be able to add callbacks/errbacks of the appropriate\r
+  # type for immediate execution? For now at least, the official answer is "don't do that."\r
+  #\r
+  def test_delayed_callbacks\r
+    s1 = 0\r
+    s2 = 0\r
+    e = 0\r
+\r
+    d = EM::DefaultDeferrable.new\r
+    d.callback {s1 += 1}\r
+\r
+    d.succeed # Triggers and discards the callback.\r
+\r
+    d.callback {s2 += 1} # This callback is executed immediately and discarded.\r
+\r
+    d.errback {e += 1} # This errback should be DISCARDED and never execute.\r
+    d.fail # To prove it, fail and assert e is 0\r
+\r
+    assert_equal( [1,1], [s1,s2] )\r
+    assert_equal( 0, e )\r
+  end\r
+\r
+  def test_timeout\r
+    n = 0\r
+    EM.run {\r
+      d = EM::DefaultDeferrable.new\r
+      d.callback {n = 1; EM.stop}\r
+      d.errback {n = 2; EM.stop}\r
+      d.timeout(1)\r
+    }\r
+    assert_equal( 2, n )\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_get_sock_opt.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_get_sock_opt.rb
new file mode 100644 (file)
index 0000000..3b6d1a5
--- /dev/null
@@ -0,0 +1,30 @@
+$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
+require 'eventmachine'
+require 'socket'
+require 'test/unit'
+
+class TestGetSockOpt < Test::Unit::TestCase
+
+  def setup
+    assert(!EM.reactor_running?)
+  end
+
+  def teardown
+    assert(!EM.reactor_running?)
+  end
+
+  #-------------------------------------
+
+  def test_get_sock_opt
+    test = self
+    EM.run do
+      EM.connect 'google.com', 80, Module.new {
+        define_method :connection_completed do
+          val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR
+          test.assert_equal "\0\0\0\0", val
+          EM.stop
+        end
+      }
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_handler_check.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_handler_check.rb
new file mode 100644 (file)
index 0000000..7f403c6
--- /dev/null
@@ -0,0 +1,37 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestHandlerCheck < Test::Unit::TestCase\r
+\r
+  class Foo < EM::Connection; end;\r
+  module TestModule; end;\r
+\r
+  def test_with_correct_class\r
+    assert_nothing_raised do\r
+      EM.run {\r
+        EM.connect("127.0.0.1", 80, Foo)\r
+        EM.stop_event_loop\r
+      }\r
+    end\r
+  end\r
+\r
+  def test_with_incorrect_class\r
+    assert_raise(ArgumentError) do\r
+      EM.run {\r
+        EM.connect("127.0.0.1", 80, String)\r
+        EM.stop_event_loop\r
+      }\r
+    end\r
+  end\r
+\r
+  def test_with_module\r
+    assert_nothing_raised do\r
+      EM.run {\r
+        EM.connect("127.0.0.1", 80, TestModule)\r
+        EM.stop_event_loop\r
+      }\r
+    end\r
+  end\r
+\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_hc.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_hc.rb
new file mode 100644 (file)
index 0000000..ff91ae9
--- /dev/null
@@ -0,0 +1,218 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+# $:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestHeaderAndContentProtocol < Test::Unit::TestCase\r
+\r
+  TestHost = "127.0.0.1"\r
+  TestPort = 8905\r
+\r
+  class SimpleTest < EventMachine::Protocols::HeaderAndContentProtocol\r
+    attr_reader :first_header, :my_headers, :request\r
+\r
+    def receive_first_header_line hdr\r
+      @first_header ||= []\r
+      @first_header << hdr\r
+    end\r
+    def receive_headers hdrs\r
+      @my_headers ||= []\r
+      @my_headers << hdrs\r
+    end\r
+    def receive_request hdrs, content\r
+      @request ||= []\r
+      @request << [hdrs, content]\r
+    end\r
+  end\r
+\r
+  def test_no_content\r
+    the_connection = nil\r
+    EventMachine.run {\r
+      EventMachine.start_server( TestHost, TestPort, SimpleTest ) do |conn|\r
+        the_connection = conn\r
+      end\r
+      EventMachine.add_timer(4) {raise "test timed out"}\r
+\r
+      client = Module.new do\r
+        def unbind\r
+          EM.add_timer(0.1) { EM.stop }\r
+        end\r
+\r
+        def post_init\r
+          send_data [ "aaa\n", "bbb\r\n", "ccc\n", "\n" ].join\r
+          close_connection_after_writing\r
+        end\r
+      end\r
+\r
+      EventMachine.connect( TestHost, TestPort, client )\r
+    }\r
+    assert_equal( ["aaa"], the_connection.first_header )\r
+    assert_equal( [%w(aaa bbb ccc)], the_connection.my_headers )\r
+    assert_equal( [[%w(aaa bbb ccc), ""]], the_connection.request )\r
+  end\r
+\r
+  def test_content\r
+    the_connection = nil\r
+    content = "A" * 50\r
+    headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]\r
+    EventMachine.run {\r
+      EventMachine.start_server( TestHost, TestPort, SimpleTest ) do |conn|\r
+        the_connection = conn\r
+      end\r
+      EventMachine.add_timer(4) { assert(false, 'test timeout'); EM.stop }\r
+\r
+      client = Module.new do\r
+        define_method(:headers) { headers }\r
+        define_method(:content) { content }\r
+\r
+        def unbind\r
+          EM.add_timer(0.1) { EM.stop }\r
+        end\r
+\r
+        def post_init\r
+          headers.each { |h| send_data "#{h}\r\n" }\r
+          send_data "\n"\r
+          send_data content\r
+          close_connection_after_writing\r
+        end\r
+      end\r
+\r
+      EventMachine.connect( TestHost, TestPort, client )\r
+\r
+    }\r
+    assert_equal( ["aaa"], the_connection.first_header )\r
+    assert_equal( [headers], the_connection.my_headers )\r
+    assert_equal( [[headers, content]], the_connection.request )\r
+  end\r
+\r
+  def test_several_requests\r
+    the_connection = nil\r
+    content = "A" * 50\r
+    headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]\r
+    EventMachine.run {\r
+      EventMachine.start_server( TestHost, TestPort, SimpleTest ) do |conn|\r
+        the_connection = conn\r
+      end\r
+      EventMachine.add_timer(4) { assert(false, 'test timeout'); EM.stop }\r
+\r
+      client = Module.new do\r
+        define_method(:headers) { headers }\r
+        define_method(:content) { content }\r
+\r
+        def unbind\r
+          EM.add_timer(0.1) { EM.stop }\r
+        end\r
+\r
+        def post_init\r
+          5.times do\r
+            headers.each { |h| send_data "#{h}\r\n" }\r
+            send_data "\n"\r
+            send_data content\r
+          end\r
+          close_connection_after_writing\r
+        end\r
+      end\r
+\r
+      EventMachine.connect( TestHost, TestPort, client )\r
+    }\r
+    assert_equal( ["aaa"] * 5, the_connection.first_header )\r
+    assert_equal( [headers] * 5, the_connection.my_headers )\r
+    assert_equal( [[headers, content]] * 5, the_connection.request )\r
+  end\r
+\r
+\r
+  # def x_test_multiple_content_length_headers\r
+  #   # This is supposed to throw a RuntimeError but it throws a C++ exception instead.\r
+  #   the_connection = nil\r
+  #   content = "A" * 50\r
+  #   headers = ["aaa", "bbb", ["Content-length: #{content.length}"]*2, "ccc"].flatten\r
+  #   EventMachine.run {\r
+  #     EventMachine.start_server( TestHost, TestPort, SimpleTest ) do |conn|\r
+  #       the_connection = conn\r
+  #     end\r
+  #     EventMachine.add_timer(4) {raise "test timed out"}\r
+  #     test_proc = proc {\r
+  #       t = TCPSocket.new TestHost, TestPort\r
+  #       headers.each {|h| t.write "#{h}\r\n" }\r
+  #       t.write "\n"\r
+  #       t.write content\r
+  #       t.close\r
+  #     }\r
+  #     EventMachine.defer test_proc, proc {\r
+  #       EventMachine.stop\r
+  #     }\r
+  #   }\r
+  # end\r
+\r
+  def test_interpret_headers\r
+    the_connection = nil\r
+    content = "A" * 50\r
+    headers = [\r
+      "GET / HTTP/1.0",\r
+      "Accept: aaa",\r
+      "User-Agent: bbb",\r
+      "Host: ccc",\r
+      "x-tempest-header:ddd"\r
+    ]\r
+\r
+    EventMachine.run {\r
+      EventMachine.start_server( TestHost, TestPort, SimpleTest ) do |conn|\r
+        the_connection = conn\r
+      end\r
+      EventMachine.add_timer(4) {raise "test timed out"}\r
+\r
+      client = Module.new do\r
+        define_method(:headers) { headers }\r
+        define_method(:content) { content }\r
+\r
+        def unbind\r
+          EM.add_timer(0.1) { EM.stop }\r
+        end\r
+\r
+        def post_init\r
+          headers.each { |h| send_data "#{h}\r\n" }\r
+          send_data "\n"\r
+          send_data content\r
+          close_connection_after_writing\r
+        end\r
+      end\r
+\r
+      EventMachine.connect( TestHost, TestPort, client )\r
+    }\r
+\r
+    hsh = the_connection.headers_2_hash( the_connection.my_headers.shift )\r
+    expect = {\r
+      :accept => "aaa",\r
+      :user_agent => "bbb",\r
+      :host => "ccc",\r
+      :x_tempest_header => "ddd"\r
+    }\r
+    assert_equal(expect, hsh)\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient.rb
new file mode 100644 (file)
index 0000000..95f68c6
--- /dev/null
@@ -0,0 +1,218 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestHttpClient < Test::Unit::TestCase\r
+\r
+    Localhost = "127.0.0.1"\r
+    Localport = 9801\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+  end\r
+\r
+  #-------------------------------------\r
+\r
+  def test_http_client\r
+    ok = false\r
+    EventMachine.run {\r
+      c = EventMachine::Protocols::HttpClient.send :request, :host => "www.bayshorenetworks.com", :port => 80\r
+      c.callback {\r
+        ok = true\r
+        EventMachine.stop\r
+      }\r
+      c.errback {EventMachine.stop} # necessary, otherwise a failure blocks the test suite forever.\r
+    }\r
+    assert ok\r
+  end\r
+\r
+  #-------------------------------------\r
+\r
+  def test_http_client_1\r
+    ok = false\r
+    EventMachine.run {\r
+      c = EventMachine::Protocols::HttpClient.send :request, :host => "www.bayshorenetworks.com", :port => 80\r
+      c.callback {ok = true; EventMachine.stop}\r
+      c.errback {EventMachine.stop}\r
+    }\r
+    assert ok\r
+  end\r
+\r
+  #-------------------------------------\r
+\r
+  def test_http_client_2\r
+    ok = false\r
+    EventMachine.run {\r
+      c = EventMachine::Protocols::HttpClient.send :request, :host => "www.bayshorenetworks.com", :port => 80\r
+      c.callback {|result|\r
+        ok = true;\r
+        EventMachine.stop\r
+      }\r
+      c.errback {EventMachine.stop}\r
+    }\r
+    assert ok\r
+  end\r
+\r
+\r
+  #-----------------------------------------\r
+\r
+  # Test a server that returns a page with a zero content-length.\r
+  # This caused an early version of the HTTP client not to generate a response,\r
+  # causing this test to hang. Observe, there was no problem with responses\r
+  # lacking a content-length, just when the content-length was zero.\r
+  #\r
+  class EmptyContent < EventMachine::Connection\r
+      def initialize *args\r
+        super\r
+      end\r
+      def receive_data data\r
+        send_data "HTTP/1.0 404 ...\r\nContent-length: 0\r\n\r\n"\r
+        close_connection_after_writing\r
+      end\r
+  end\r
+\r
+  def test_http_empty_content\r
+      ok = false\r
+      EventMachine.run {\r
+        EventMachine.start_server "127.0.0.1", 9701, EmptyContent\r
+        c = EventMachine::Protocols::HttpClient.send :request, :host => "127.0.0.1", :port => 9701\r
+        c.callback {|result|\r
+          ok = true\r
+          EventMachine.stop\r
+        }\r
+      }\r
+      assert ok\r
+  end\r
+\r
+\r
+  #---------------------------------------\r
+\r
+  class PostContent < EventMachine::Protocols::LineAndTextProtocol\r
+      def initialize *args\r
+        super\r
+        @lines = []\r
+      end\r
+      def receive_line line\r
+        if line.length > 0\r
+          @lines << line\r
+        else\r
+          process_headers\r
+        end\r
+      end\r
+      def receive_binary_data data\r
+        @post_content = data\r
+        send_response\r
+      end\r
+      def process_headers\r
+        if @lines.first =~ /\APOST ([^\s]+) HTTP\/1.1\Z/\r
+          @uri = $1.dup\r
+        else\r
+          raise "bad request"\r
+        end\r
+\r
+        @lines.each {|line|\r
+          if line =~ /\AContent-length:\s*(\d+)\Z/i\r
+            @content_length = $1.dup.to_i\r
+          elsif line =~ /\AContent-type:\s*(\d+)\Z/i\r
+            @content_type = $1.dup\r
+          end\r
+        }\r
+\r
+        raise "invalid content length" unless @content_length\r
+        set_binary_mode @content_length\r
+      end\r
+      def send_response\r
+        send_data "HTTP/1.1 200 ...\r\nConnection: close\r\nContent-length: 10\r\nContent-type: text/html\r\n\r\n0123456789"\r
+        close_connection_after_writing\r
+      end\r
+  end\r
+\r
+  # TODO, this is WRONG. The handler is asserting an HTTP 1.1 request, but the client\r
+  # is sending a 1.0 request. Gotta fix the client\r
+  def test_post\r
+      response = nil\r
+      EventMachine.run {\r
+        EventMachine.start_server Localhost, Localport, PostContent\r
+        EventMachine.add_timer(2) {raise "timed out"}\r
+        c = EventMachine::Protocols::HttpClient.request(\r
+          :host=>Localhost,\r
+          :port=>Localport,\r
+          :method=>:post,\r
+          :request=>"/aaa",\r
+          :content=>"XYZ",\r
+          :content_type=>"text/plain"\r
+        )\r
+        c.callback {|r|\r
+          response = r\r
+          EventMachine.stop\r
+        }\r
+      }\r
+\r
+      assert_equal( 200, response[:status] )\r
+      assert_equal( "0123456789", response[:content] )\r
+  end\r
+\r
+\r
+  # TODO, need a more intelligent cookie tester.\r
+  # In fact, this whole test-harness needs a beefier server implementation.\r
+  def test_cookie\r
+    ok = false\r
+    EM.run {\r
+      c = EM::Protocols::HttpClient.send :request, :host => "www.bayshorenetworks.com", :port => 80, :cookie=>"aaa=bbb"\r
+      c.callback {|result|\r
+        ok = true;\r
+        EventMachine.stop\r
+      }\r
+      c.errback {EventMachine.stop}\r
+    }\r
+    assert ok\r
+  end\r
+\r
+  # We can tell the client to send an HTTP/1.0 request (default is 1.1).\r
+  # This is useful for suppressing chunked responses until those are working.\r
+  def test_version_1_0\r
+    ok = false\r
+    EM.run {\r
+      c = EM::P::HttpClient.request(\r
+        :host => "www.bayshorenetworks.com",\r
+        :port => 80,\r
+        :version => "1.0"\r
+      )\r
+      c.callback {|result|\r
+        ok = true;\r
+        EventMachine.stop\r
+      }\r
+      c.errback {EventMachine.stop}\r
+    }\r
+    assert ok\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient2.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_httpclient2.rb
new file mode 100644 (file)
index 0000000..cec6b95
--- /dev/null
@@ -0,0 +1,153 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestHttpClient2 < Test::Unit::TestCase
+  Localhost = "127.0.0.1"
+  Localport = 9801
+
+  def setup
+  end
+
+  def teardown
+  end
+
+
+  class TestServer < EM::Connection
+  end
+
+  # #connect returns an object which has made a connection to an HTTP server
+  # and exposes methods for making HTTP requests on that connection.
+  # #connect can take either a pair of parameters (a host and a port),
+  # or a single parameter which is a Hash.
+  #
+  def test_connect
+    EM.run {
+      EM.start_server Localhost, Localport, TestServer
+      http1 = EM::P::HttpClient2.connect Localhost, Localport
+      http2 = EM::P::HttpClient2.connect( :host=>Localhost, :port=>Localport )
+      EM.stop
+    }
+  end
+
+
+  def test_bad_port
+    EM.run {
+      EM.start_server Localhost, Localport, TestServer
+      assert_raises( ArgumentError ) {
+        EM::P::HttpClient2.connect Localhost, "xxx"
+      }
+      EM.stop
+    }
+  end
+
+  def test_bad_server
+    err = nil
+    EM.run {
+      http = EM::P::HttpClient2.connect Localhost, 9999
+      d = http.get "/"
+      d.errback { err = true; d.internal_error; EM.stop }
+    }
+    assert(err)
+  end
+
+  def test_get
+    content = nil
+    EM.run {
+      http = EM::P::HttpClient2.connect "google.com", 80
+      d = http.get "/"
+      d.callback {
+        content = d.content
+        EM.stop
+      }
+    }
+    assert(content)
+  end
+
+  # Not a pipelined request because we wait for one response before we request the next.
+  # XXX this test is broken because it sends the second request to the first connection
+  # XXX right before the connection closes
+  def _test_get_multiple
+    content = nil
+    EM.run {
+      http = EM::P::HttpClient2.connect "google.com", 80
+      d = http.get "/"
+      d.callback {
+        e = http.get "/"
+        e.callback {
+          content = e.content
+          EM.stop
+        }
+      }
+    }
+    assert(content)
+  end
+
+  def test_get_pipeline
+    headers, headers2 = nil, nil
+    EM.run {
+      http = EM::P::HttpClient2.connect "google.com", 80
+      d = http.get("/")
+      d.callback {
+        headers = d.headers
+      }
+      e = http.get("/")
+      e.callback {
+        headers2 = e.headers
+      }
+      EM::Timer.new(1) {EM.stop}
+    }
+    assert(headers)
+    assert(headers2)
+  end
+
+
+  def test_authheader
+    EM.run {
+      EM.start_server Localhost, Localport, TestServer
+      http = EM::P::HttpClient2.connect Localhost, 18842
+      d = http.get :url=>"/", :authorization=>"Basic xxx"
+      d.callback {EM.stop}
+      d.errback {EM.stop}
+    }
+  end
+
+  def test_https_get
+    d = nil
+    EM.run {
+      http = EM::P::HttpClient2.connect :host => 'www.amazon.com', :port => 443, :ssl => true
+      d = http.get "/"
+      d.callback {
+        EM.stop
+      }
+    }
+    assert_equal(200, d.status)
+  end if EM.ssl?
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_inactivity_timeout.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_inactivity_timeout.rb
new file mode 100644 (file)
index 0000000..04ae3d8
--- /dev/null
@@ -0,0 +1,50 @@
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestInactivityTimeout < Test::Unit::TestCase
+
+  def test_default
+    $timeout = nil
+    EM.run {
+      c = EM.connect("127.0.0.1", 54321)
+      $timeout = c.comm_inactivity_timeout
+      EM.stop
+    }
+
+    assert_equal(0.0, $timeout)
+  end
+
+  def test_set_and_get
+    $timeout = nil
+    EM.run {
+      c = EM.connect("127.0.0.1", 54321)
+      c.comm_inactivity_timeout = 2.5
+      $timeout = c.comm_inactivity_timeout
+      EM.stop
+    }
+
+    assert_equal(2.5, $timeout)
+  end
+
+  module TimeoutHandler
+    def unbind
+      EM.stop
+    end
+  end
+
+  def test_for_real
+    EM.run {
+      EM.heartbeat_interval = 0.1
+      EM.start_server("127.0.0.1", 12345)
+      EM.add_timer(0.2) {
+        $start = Time.now
+        c = EM.connect("127.0.0.1", 12345, TimeoutHandler)
+        c.comm_inactivity_timeout = 2.5
+      }
+    }
+
+    assert_in_delta(2.5, (Time.now - $start), 0.3)
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_kb.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_kb.rb
new file mode 100644 (file)
index 0000000..ba4d2af
--- /dev/null
@@ -0,0 +1,60 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestKeyboardEvents < Test::Unit::TestCase\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+  end\r
+\r
+  module KbHandler\r
+    include EM::Protocols::LineText2\r
+    def receive_line d\r
+      EM::stop if d == "STOP"\r
+    end\r
+  end\r
+\r
+  # This test doesn't actually do anything useful but is here to\r
+  # illustrate the usage. If you removed the timer and ran this test\r
+  # by itself on a console, and then typed into the console, it would\r
+  # work.\r
+  # I don't know how to get the test harness to simulate actual keystrokes.\r
+  # When someone figures that out, then we can make this a real test.\r
+  #\r
+  def test_kb\r
+    EM.run {\r
+      EM.open_keyboard KbHandler\r
+      EM::Timer.new(1) { EM.stop }\r
+    } if $stdout.tty? # don't run the test unless it stands a chance of validity.\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp.rb
new file mode 100644 (file)
index 0000000..ff7b978
--- /dev/null
@@ -0,0 +1,182 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+#
+
+require 'eventmachine'
+require 'test/unit'
+
+class TestLineAndTextProtocol < Test::Unit::TestCase
+
+  TestHost = "127.0.0.1"
+  TestPort = 8905
+
+
+  #--------------------------------------------------------------------
+
+  class SimpleLineTest < EventMachine::Protocols::LineAndTextProtocol
+    def receive_line line
+      @line_buffer << line
+    end
+  end
+
+  module StopClient
+    def set_receive_data(&blk)
+      @rdb = blk
+    end
+    
+    def receive_data data
+      @rdb.call(data) if @rdb
+    end
+    
+    def unbind
+      EM.add_timer(0.1) { EM.stop }
+    end
+  end
+
+
+  def test_simple_lines
+    lines_received = []
+    EventMachine.run {
+      EventMachine.start_server( TestHost, TestPort, SimpleLineTest ) do |conn|
+        conn.instance_eval "@line_buffer = lines_received"
+      end
+      EventMachine.add_timer(4) {assert(false, "test timed out")}
+
+      EventMachine.connect TestHost, TestPort, StopClient do |c|
+        c.send_data "aaa\nbbb\r\nccc\n"
+        c.close_connection_after_writing
+      end
+    }
+    assert_equal( %w(aaa bbb ccc), lines_received )
+  end
+
+  #--------------------------------------------------------------------
+
+  class SimpleLineTest < EventMachine::Protocols::LineAndTextProtocol
+    def receive_error text
+      @error_message << text
+    end
+  end
+
+  def test_overlength_lines
+    lines_received = []
+    EventMachine.run {
+      EventMachine.start_server( TestHost, TestPort, SimpleLineTest ) do |conn|
+        conn.instance_eval "@error_message = lines_received"
+      end
+      EventMachine.add_timer(4) {assert(false, "test timed out")}
+
+      EventMachine.connect TestHost, TestPort, StopClient do |c|
+        c.send_data "a" * (16*1024 + 1)
+        c.send_data "\n"
+        c.close_connection_after_writing
+      end
+
+    }
+    assert_equal( ["overlength line"], lines_received )
+  end
+
+
+  #--------------------------------------------------------------------
+
+  class LineAndTextTest < EventMachine::Protocols::LineAndTextProtocol
+    def post_init
+    end
+    def receive_line line
+      if line =~ /content-length:\s*(\d+)/i
+        @content_length = $1.to_i
+      elsif line.length == 0
+        set_binary_mode @content_length
+      end
+    end
+    def receive_binary_data text
+      send_data "received #{text.length} bytes"
+      close_connection_after_writing
+    end
+  end
+
+  def test_lines_and_text
+    output = ''
+    lines_received = []
+    text_received = []
+    EventMachine.run {
+      EventMachine.start_server( TestHost, TestPort, LineAndTextTest ) do |conn|
+        conn.instance_eval "@lines = lines_received; @text = text_received"
+      end
+      EventMachine.add_timer(4) {assert(false, "test timed out")}
+
+      EventMachine.connect TestHost, TestPort, StopClient do |c|
+        c.set_receive_data { |data| output << data }
+        c.send_data "Content-length: 400\n"
+        c.send_data "\n"
+        c.send_data "A" * 400
+        EM.add_timer(0.1) { c.close_connection_after_writing }
+      end
+    }
+    assert_equal( "received 400 bytes", output )
+  end
+
+  #--------------------------------------------------------------------
+
+
+  class BinaryTextTest < EventMachine::Protocols::LineAndTextProtocol
+    def post_init
+    end
+    def receive_line line
+      if line =~ /content-length:\s*(\d+)/i
+        set_binary_mode $1.to_i
+      else
+        raise "protocol error"
+      end
+    end
+    def receive_binary_data text
+      send_data "received #{text.length} bytes"
+      close_connection_after_writing
+    end
+  end
+
+  def test_binary_text
+    output = ''
+    lines_received = []
+    text_received = []
+    EventMachine.run {
+      EventMachine.start_server( TestHost, TestPort, BinaryTextTest ) do |conn|
+        conn.instance_eval "@lines = lines_received; @text = text_received"
+      end
+      EventMachine.add_timer(4) {assert(false, "test timed out")}
+
+      EventMachine.connect TestHost, TestPort, StopClient do |c|
+        c.set_receive_data { |data| output << data }
+        c.send_data "Content-length: 10000\n"
+        c.send_data "A" * 10000
+        EM.add_timer(0.2) { c.close_connection_after_writing }
+      end
+    }
+    assert_equal( "received 10000 bytes", output )
+  end
+
+  #--------------------------------------------------------------------
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp2.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ltp2.rb
new file mode 100644 (file)
index 0000000..8bfaf5c
--- /dev/null
@@ -0,0 +1,317 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+# TODO!!! Need tests for overlength headers and text bodies.\r
+\r
+class TestLineText2 < Test::Unit::TestCase\r
+\r
+  # Run each of these tests two ways: passing in the whole test-dataset in one chunk,\r
+  # and passing it in one character at a time.\r
+\r
+  class Basic\r
+    include EM::Protocols::LineText2\r
+    attr_reader :lines\r
+    def receive_line line\r
+      (@lines ||= []) << line\r
+    end\r
+  end\r
+  def test_basic\r
+    testdata = "Line 1\nLine 2\r\nLine 3\n"\r
+\r
+    a = Basic.new\r
+    a.receive_data testdata\r
+    assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )\r
+\r
+    a = Basic.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )\r
+  end\r
+\r
+  class ChangeDelimiter\r
+    include EM::Protocols::LineText2\r
+    attr_reader :lines\r
+    def initialize *args\r
+      super\r
+      @delim = "A"\r
+      set_delimiter @delim\r
+    end\r
+    def receive_line line\r
+      (@lines ||= []) << line\r
+      set_delimiter( @delim.succ! )\r
+    end\r
+  end\r
+\r
+  def test_change_delimiter\r
+    testdata = %Q(LineaALinebBLinecCLinedD)\r
+\r
+    a = ChangeDelimiter.new\r
+    a.receive_data testdata\r
+    assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )\r
+\r
+    a = ChangeDelimiter.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )\r
+  end\r
+\r
+\r
+  #--\r
+  # Test two lines followed by an empty line, ten bytes of binary data, then\r
+  # two more lines.\r
+\r
+  class Binary\r
+    include EM::Protocols::LineText2\r
+    attr_reader :lines, :body\r
+    def initialize *args\r
+      super\r
+      @lines = []\r
+      @body = nil\r
+    end\r
+    def receive_line ln\r
+      if ln == ""\r
+        set_text_mode 10\r
+      else\r
+        @lines << ln\r
+      end\r
+    end\r
+    def receive_binary_data data\r
+      @body = data\r
+    end\r
+  end\r
+\r
+  def test_binary\r
+    testdata = %Q(Line 1\r
+Line 2\r
+\r
+0000000000Line 3\r
+Line 4\r
+)\r
+\r
+    a = Binary.new\r
+    a.receive_data testdata\r
+    assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)\r
+    assert_equal( "0000000000", a.body )\r
+\r
+    a = Binary.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)\r
+    assert_equal( "0000000000", a.body )\r
+  end\r
+\r
+\r
+  # Test unsized binary data. The expectation is that each chunk of it\r
+  # will be passed to us as it it received.\r
+  class UnsizedBinary\r
+    include EM::Protocols::LineText2\r
+    attr_reader :n_calls, :body\r
+    def initialize *args\r
+      super\r
+      set_text_mode\r
+    end\r
+    def receive_binary_data data\r
+      @n_calls ||= 0\r
+      @n_calls += 1\r
+      (@body ||= "") << data\r
+    end\r
+  end\r
+\r
+  def test_unsized_binary\r
+    testdata = "X\0" * 1000\r
+\r
+    a = UnsizedBinary.new\r
+    a.receive_data testdata\r
+    assert_equal( 1, a.n_calls )\r
+    assert_equal( testdata, a.body )\r
+\r
+    a = UnsizedBinary.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( 2000, a.n_calls )\r
+    assert_equal( testdata, a.body )\r
+  end\r
+\r
+\r
+  # Test binary data with a "throw back" into line-mode.\r
+  class ThrowBack\r
+    include EM::Protocols::LineText2\r
+    attr_reader :headers\r
+    def initialize *args\r
+      super\r
+      @headers = []\r
+      @n_bytes = 0\r
+      set_text_mode\r
+    end\r
+    def receive_binary_data data\r
+      wanted = 25 - @n_bytes\r
+      will_take = if data.length > wanted\r
+                    data.length - wanted\r
+                  else\r
+                    data.length\r
+                  end\r
+      @n_bytes += will_take\r
+\r
+      if @n_bytes == 25\r
+        set_line_mode( data[will_take..-1] )\r
+      end\r
+    end\r
+    def receive_line ln\r
+      @headers << ln\r
+    end\r
+  end\r
+  def test_throw_back\r
+    testdata = "Line\n" * 10\r
+\r
+    a = ThrowBack.new\r
+    a.receive_data testdata\r
+    assert_equal( ["Line"] * 5, a.headers )\r
+\r
+    a = ThrowBack.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( ["Line"] * 5, a.headers )\r
+  end\r
+\r
+  # Test multi-character line delimiters.\r
+  # Also note that the test data has a "tail" with no delimiter, that will be\r
+  # discarded, but cf. the BinaryTail test.\r
+  # TODO!!! This test doesn't work in the byte-by-byte case.\r
+  class Multichar\r
+    include EM::Protocols::LineText2\r
+    attr_reader :lines\r
+    def initialize *args\r
+      super\r
+      @lines = []\r
+      set_delimiter "012"\r
+    end\r
+    def receive_line ln\r
+      @lines << ln\r
+    end\r
+  end\r
+  def test_multichar\r
+    testdata = "Line012Line012Line012Line"\r
+\r
+    a = Multichar.new\r
+    a.receive_data testdata\r
+    assert_equal( ["Line"]*3, a.lines )\r
+\r
+    a = Multichar.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    # DOESN'T WORK in this case. Multi-character delimiters are broken.\r
+    #assert_equal( ["Line"]*3, a.lines )\r
+  end\r
+\r
+  # Test a binary "tail," when a sized binary transfer doesn't complete because\r
+  # of an unbind. We get a partial result.\r
+  class BinaryTail\r
+    include EM::Protocols::LineText2\r
+    attr_reader :data\r
+    def initialize *args\r
+      super\r
+      @data = ""\r
+      set_text_mode 1000\r
+    end\r
+    def receive_binary_data data\r
+      # we expect to get all the data in one chunk, even in the byte-by-byte case,\r
+      # because sized transfers by definition give us exactly one call to\r
+      # #receive_binary_data.\r
+      @data = data\r
+    end\r
+  end\r
+  def test_binary_tail\r
+    testdata = "0" * 500\r
+\r
+    a = BinaryTail.new\r
+    a.receive_data testdata\r
+    a.unbind\r
+    assert_equal( "0" * 500, a.data )\r
+\r
+    a = BinaryTail.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    a.unbind\r
+    assert_equal( "0" * 500, a.data )\r
+  end\r
+\r
+\r
+  # Test an end-of-binary call. Arrange to receive binary data but don't bother counting it\r
+  # as it comes. Rely on getting receive_end_of_binary_data to signal the transition back to\r
+  # line mode.\r
+  # At the present time, this isn't strictly necessary with sized binary chunks because by\r
+  # definition we accumulate them and make exactly one call to receive_binary_data, but\r
+  # we may want to support a mode in the future that would break up large chunks into multiple\r
+  # calls.\r
+  class LazyBinary\r
+    include EM::Protocols::LineText2\r
+    attr_reader :data, :end\r
+    def initialize *args\r
+      super\r
+      @data = ""\r
+      set_text_mode 1000\r
+    end\r
+    def receive_binary_data data\r
+      # we expect to get all the data in one chunk, even in the byte-by-byte case,\r
+      # because sized transfers by definition give us exactly one call to\r
+      # #receive_binary_data.\r
+      @data = data\r
+    end\r
+    def receive_end_of_binary_data\r
+      @end = true\r
+    end\r
+  end\r
+  def test_receive_end_of_binary_data\r
+    testdata = "_" * 1000\r
+    a = LazyBinary.new\r
+    testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }\r
+    assert_equal( "_" * 1000, a.data )\r
+    assert( a.end )\r
+  end\r
+\r
+\r
+  # This tests a bug fix in which calling set_text_mode failed when called\r
+  # inside receive_binary_data.\r
+  #\r
+  class BinaryPair\r
+    include EM::Protocols::LineText2\r
+    attr_reader :sizes\r
+    def initialize *args\r
+      super\r
+      set_text_mode 1\r
+      @sizes = []\r
+    end\r
+    def receive_binary_data dt\r
+      @sizes <<  dt.length\r
+      set_text_mode( (dt.length == 1) ? 2 : 1 )\r
+    end\r
+  end\r
+  def test_binary_pairs\r
+    test_data = "123" * 5\r
+    a = BinaryPair.new\r
+    a.receive_data test_data\r
+    assert_equal( [1,2,1,2,1,2,1,2,1,2], a.sizes )\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_next_tick.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_next_tick.rb
new file mode 100644 (file)
index 0000000..42667e9
--- /dev/null
@@ -0,0 +1,133 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+#
+
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestNextTick < Test::Unit::TestCase
+
+  def test_tick_arg
+    pr = proc {EM.stop}
+    EM.run {
+      EM.next_tick pr
+    }
+    assert true
+  end
+
+  def test_tick_block
+    EM.run {
+      EM.next_tick {EM.stop}
+    }
+    assert true
+  end
+
+  # This illustrates the solution to a long-standing problem.
+  # It's now possible to correctly nest calls to EM#run.
+  # See the source code commentary for EM#run for more info.
+  #
+  def test_run_run
+    EM.run {
+      EM.run {
+        EM.next_tick {EM.stop}
+      }
+    }
+  end
+
+  def test_pre_run_queue
+    x = false
+    EM.next_tick { EM.stop; x = true }
+    EM.run { EM.add_timer(0.2) { EM.stop } }
+    assert x
+  end
+
+  def test_cleanup_after_stop
+    x = true
+    EM.run{
+      EM.next_tick{
+        EM.stop
+        EM.next_tick{ x=false }
+      }
+    }
+    EM.run{
+      EM.next_tick{ EM.stop }
+    }
+    assert x
+  end
+
+  # We now support an additional parameter for EM#run.
+  # You can pass two procs to EM#run now. The first is executed as the normal
+  # run block. The second (if given) is scheduled for execution after the
+  # reactor loop completes.
+  # The reason for supporting this is subtle. There has always been an expectation
+  # that EM#run doesn't return until after the reactor loop ends. But now it's
+  # possible to nest calls to EM#run, which means that a nested call WILL
+  # RETURN. In order to write code that will run correctly either way, it's
+  # recommended to put any code which must execute after the reactor completes
+  # in the second parameter.
+  #
+  def test_run_run_2
+    a = proc {EM.stop}
+    b = proc {assert true}
+    EM.run a, b
+  end
+
+
+  # This illustrates that EM#run returns when it's called nested.
+  # This isn't a feature, rather it's something to be wary of when writing code
+  # that must run correctly even if EM#run is called while a reactor is already
+  # running.
+  def test_run_run_3
+    a = []
+    EM.run {
+      EM.run proc {EM.stop}, proc {a << 2}
+      a << 1
+    }
+    assert_equal( [1,2], a )
+  end
+
+
+  def test_schedule_on_reactor_thread
+    x = false
+    EM.run do
+      EM.schedule { x = true }
+      EM.stop
+    end
+    assert x
+  end
+
+  def test_schedule_from_thread
+    x = false
+    EM.run do
+      Thread.new { EM.schedule { x = true } }.join
+      assert !x
+      EM.next_tick { EM.stop }
+    end
+    assert x
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_object_protocol.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_object_protocol.rb
new file mode 100644 (file)
index 0000000..7d845d4
--- /dev/null
@@ -0,0 +1,37 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestObjectProtocol < Test::Unit::TestCase\r
+  Host = "127.0.0.1"\r
+  Port = 9550\r
+\r
+  module Server\r
+    include EM::P::ObjectProtocol\r
+    def post_init\r
+      send_object :hello=>'world'\r
+    end\r
+    def receive_object obj\r
+      $server = obj\r
+      EM.stop\r
+    end\r
+  end\r
+\r
+  module Client\r
+    include EM::P::ObjectProtocol\r
+    def receive_object obj\r
+      $client = obj\r
+      send_object 'you_said'=>obj\r
+    end\r
+  end\r
+\r
+  def test_send_receive\r
+    EM.run{\r
+      EM.start_server Host, Port, Server\r
+      EM.connect Host, Port, Client\r
+    }\r
+\r
+    assert($client == {:hello=>'world'})\r
+    assert($server == {'you_said'=>{:hello=>'world'}})\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pause.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pause.rb
new file mode 100644 (file)
index 0000000..ac5def8
--- /dev/null
@@ -0,0 +1,70 @@
+$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
+require 'eventmachine'
+require 'socket'
+require 'test/unit'
+
+class TestPause < Test::Unit::TestCase
+  TestHost = "127.0.0.1"
+  TestPort = 9070
+
+  def setup
+    assert(!EM.reactor_running?)
+  end
+
+  def teardown
+    assert(!EM.reactor_running?)
+  end
+
+  #-------------------------------------
+
+  def test_pause_resume
+    test = self
+    server = nil
+
+    s_rx = c_rx = 0
+
+    EM.run do
+      EM.start_server TestHost, TestPort, Module.new {
+        define_method :post_init do
+          server = self
+        end
+
+        define_method :receive_data do |data|
+          s_rx += 1
+
+          EM.add_periodic_timer(0.01) { send_data 'hi' }
+          send_data 'hi'
+
+          # pause server, now no outgoing data will actually
+          # be sent and no more incoming data will be received
+          pause
+        end
+      }
+
+      c = EM.connect TestHost, TestPort, Module.new {
+        define_method :receive_data do |data|
+          c_rx += 1
+        end
+      }
+      EM.add_periodic_timer(0.01) { c.send_data 'hi' }
+
+      EM.add_timer(1) do
+        test.assert_equal 1, s_rx
+        test.assert_equal 0, c_rx
+        test.assert server.paused?
+
+        # resume server, queued outgoing and incoming data will be flushed
+        server.resume
+
+        test.assert ! server.paused?
+
+        EM.add_timer(1) do
+          test.assert server.paused?
+          test.assert s_rx >= 2
+          test.assert c_rx >= 1
+          EM.stop_event_loop
+        end
+      end
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pending_connect_timeout.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pending_connect_timeout.rb
new file mode 100644 (file)
index 0000000..9cafed9
--- /dev/null
@@ -0,0 +1,48 @@
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestPendingConnectTimeout < Test::Unit::TestCase
+
+  def test_default
+    $timeout = nil
+    EM.run {
+      c = EM.connect("127.0.0.1", 54321)
+      $timeout = c.pending_connect_timeout
+      EM.stop
+    }
+
+    assert_equal(20.0, $timeout)
+  end
+
+  def test_set_and_get
+    $timeout = nil
+    EM.run {
+      c = EM.connect("1.2.3.4", 54321)
+      c.pending_connect_timeout = 2.5
+      $timeout = c.pending_connect_timeout
+      EM.stop
+    }
+
+    assert_equal(2.5, $timeout)
+  end
+
+  module TimeoutHandler
+    def unbind
+      EM.stop
+    end
+  end
+
+  def test_for_real
+    $timeout = nil
+    EM.run {
+      EM.heartbeat_interval = 0.1
+      $start = Time.now
+      c = EM.connect("1.2.3.4", 54321, TimeoutHandler)
+      c.pending_connect_timeout = 5
+    }
+
+    assert_in_delta(5, (Time.now - $start), 0.3)
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_process_watch.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_process_watch.rb
new file mode 100644 (file)
index 0000000..8fa2954
--- /dev/null
@@ -0,0 +1,48 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestProcessWatch < Test::Unit::TestCase\r
+  module ParentProcessWatcher\r
+    def process_forked\r
+      $forked = true\r
+    end\r
+  end\r
+\r
+  module ChildProcessWatcher\r
+    def process_exited\r
+      $exited = true\r
+    end\r
+    def unbind\r
+      $unbind = true\r
+      EM.stop\r
+    end\r
+  end\r
+\r
+  def setup\r
+    EM.kqueue = true if EM.kqueue?\r
+  end\r
+\r
+  def teardown\r
+    EM.kqueue = false if EM.kqueue?\r
+  end\r
+\r
+  def test_events\r
+    EM.run{\r
+      # watch ourselves for a fork notification\r
+      EM.watch_process(Process.pid, ParentProcessWatcher)\r
+      $fork_pid = fork{ sleep }\r
+      child = EM.watch_process($fork_pid, ChildProcessWatcher)\r
+      $pid = child.pid\r
+\r
+      EM.add_timer(0.5){\r
+        Process.kill('TERM', $fork_pid)\r
+      }\r
+    }\r
+\r
+    assert_equal($pid, $fork_pid)\r
+    assert($forked)\r
+    assert($exited)\r
+    assert($unbind)\r
+  end\r
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_processes.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_processes.rb
new file mode 100644 (file)
index 0000000..e2968aa
--- /dev/null
@@ -0,0 +1,128 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestProcesses < Test::Unit::TestCase
+
+  # EM::DeferrableChildProcess is a sugaring of a common use-case
+  # involving EM::popen.
+  # Call the #open method on EM::DeferrableChildProcess, passing
+  # a command-string. #open immediately returns an EM::Deferrable
+  # object. It also schedules the forking of a child process, which
+  # will execute the command passed to #open.
+  # When the forked child terminates, the Deferrable will be signalled
+  # and execute its callbacks, passing the data that the child process
+  # wrote to stdout.
+  #
+  def test_deferrable_child_process
+    ls = ""
+    EM.run {
+      d = EM::DeferrableChildProcess.open( "ls -ltr" )
+      d.callback {|data_from_child|
+        ls = data_from_child
+        EM.stop
+      }
+    }
+    assert( ls.length > 0)
+  end
+
+  def setup
+    $out = nil
+    $status = nil
+  end
+
+  def test_em_system
+    EM.run{
+      EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop }
+    }
+
+    assert( $out.length > 0 )
+    assert_equal($status.exitstatus, 0)
+    assert_equal($status.class, Process::Status)
+  end
+
+  def test_em_system_pid
+    $pids = []
+
+    EM.run{
+      $pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop })
+    }
+
+    assert_equal $pids[0], $pids[1]
+  end
+
+  def test_em_system_with_proc
+    EM.run{
+      EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop })
+    }
+
+    assert( $out.length > 0 )
+    assert_equal($status.exitstatus, 0)
+    assert_equal($status.class, Process::Status)
+  end
+
+  def test_em_system_with_two_procs
+    EM.run{
+      EM.system('sh', proc{ |process|
+        process.send_data("echo hello\n")
+        process.send_data("exit\n")
+      }, proc{ |out,status|
+        $out = out
+        $status = status
+        EM.stop
+      })
+    }
+
+    assert_equal("hello\n", $out)
+  end
+
+  def test_em_system_cmd_arguments
+    EM.run{
+      EM.system('sh', '--version', proc{ |process|
+      }, proc{ |out,status|
+        $out = out
+        $status = status
+        EM.stop
+      })
+    }
+
+    assert_match(/version/i, $out)
+  end
+
+  def test_em_system_spaced_arguments
+    EM.run{
+      EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status|
+        $out = out
+        EM.stop
+      })
+    }
+
+    assert_equal("hello\n", $out)
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_proxy_connection.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_proxy_connection.rb
new file mode 100644 (file)
index 0000000..3bc6434
--- /dev/null
@@ -0,0 +1,92 @@
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestProxyConnection < Test::Unit::TestCase
+
+  module ProxyConnection
+    def initialize(client, request)
+      @client, @request = client, request
+    end
+
+    def post_init
+      EM::enable_proxy(self, @client)
+    end
+
+    def connection_completed
+      EM.next_tick {
+        send_data @request
+      }
+    end
+
+    def proxy_target_unbound
+      $unbound_early = true
+      EM.stop
+    end
+
+    def unbind
+      @client.close_connection_after_writing
+    end
+  end
+
+  module Client
+    def connection_completed
+      send_data "EventMachine rocks!"
+    end
+
+    def receive_data(data)
+      $client_data = data
+    end
+
+    def unbind
+      EM.stop
+    end
+  end
+
+  module Client2
+    include Client
+    def unbind; end
+  end
+
+  module Server
+    def receive_data(data)
+      send_data "I know!" if data == "EventMachine rocks!"
+      close_connection_after_writing
+    end
+  end
+
+  module ProxyServer
+    def receive_data(data)
+      EM.connect("127.0.0.1", 54321, ProxyConnection, self, data)
+    end
+  end
+
+  module EarlyClosingProxy
+    def receive_data(data)
+      EM.connect("127.0.0.1", 54321, ProxyConnection, self, data)
+      close_connection
+    end
+  end
+
+  def test_proxy_connection
+    EM.run {
+      EM.start_server("127.0.0.1", 54321, Server)
+      EM.start_server("127.0.0.1", 12345, ProxyServer)
+      EM.connect("127.0.0.1", 12345, Client)
+    }
+
+    assert_equal("I know!", $client_data)
+  end
+
+  def test_early_close
+    $client_data = nil
+    EM.run {
+      EM.start_server("127.0.0.1", 54321, Server)
+      EM.start_server("127.0.0.1", 12345, EarlyClosingProxy)
+      EM.connect("127.0.0.1", 12345, Client2)
+    }
+
+    assert($unbound_early)
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pure.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_pure.rb
new file mode 100644 (file)
index 0000000..bb08f80
--- /dev/null
@@ -0,0 +1,125 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestPure < Test::Unit::TestCase\r
+\r
+\r
+  Host,Port = "0.0.0.0", 9060\r
+\r
+\r
+  # These tests are intended to exercise problems that come up in the\r
+  # pure-Ruby implementation. However, we DON'T constrain them such that\r
+  # they only run in pure-Ruby. These tests need to work identically in\r
+  # any implementation.\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+  end\r
+\r
+  #-------------------------------------\r
+\r
+  # The EM reactor needs to run down open connections and release other resources\r
+  # when it stops running. Make sure this happens even if user code throws a Ruby\r
+  # exception.\r
+  # One way to see this is to run identical tests that open a TCP server and throw\r
+  # an exception. (We do this twice because an exception aborts a test. We make the\r
+  # two tests identical except for the method name because we can't predict the order\r
+  # in which the test harness will run them.)\r
+  # If exception handling is incorrect, the second test will fail with a no-bind error\r
+  # because the TCP server opened in the first test will not have been closed.\r
+  #\r
+  def run_exception\r
+      EM.run {\r
+        EM.start_server Host, Port\r
+        raise "an exception"\r
+      }\r
+  end\r
+  def test_exception_1\r
+    assert_raises( RuntimeError ) { run_exception }\r
+  end\r
+  def test_exception_2\r
+    ex_class = RUBY_PLATFORM == 'java' ? NativeException : RuntimeError\r
+    assert_raises( ex_class ) { run_exception }\r
+  end\r
+\r
+\r
+  # Under some circumstances, the pure Ruby library would emit an Errno::ECONNREFUSED\r
+  # exception on certain kinds of TCP connect-errors.\r
+  # It's always been something of an open question whether EM should throw an exception\r
+  # in these cases but the defined answer has always been to catch it the unbind method.\r
+  # With a connect failure, the latter will always fire, but connection_completed will\r
+  # never fire. So even though the point is arguable, it's incorrect for the pure Ruby\r
+  # version to throw an exception.\r
+  module TestConnrefused\r
+    def unbind\r
+      EM.stop\r
+    end\r
+    def connection_completed\r
+      raise "should never get here"\r
+    end\r
+  end\r
+  def test_connrefused\r
+    EM.run {\r
+      EM.connect "0.0.0.0", 60001, TestConnrefused\r
+    }\r
+  end\r
+\r
+\r
+  # Make sure connection_completed gets called as expected with TCP clients. This is the\r
+  # opposite of test_connrefused.\r
+  # If the test fails, it will hang because EM.stop never gets called.\r
+  #\r
+  module TestConnaccepted\r
+    def connection_completed\r
+      EM.stop\r
+    end\r
+  end\r
+  def test_connaccepted\r
+    timeout = false\r
+    EM.run {\r
+      EM.start_server "0.0.0.0", 60002\r
+      EM.connect "0.0.0.0", 60002, TestConnaccepted\r
+      EM::Timer.new(1) {timeout = true; EM.stop}\r
+    }\r
+    assert_equal( false, timeout )\r
+  end\r
+\r
+  def test_reactor_running\r
+    a = false\r
+    EM.run {\r
+      a = EM.reactor_running?\r
+      EM.next_tick {EM.stop}\r
+    }\r
+    assert a\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_queue.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_queue.rb
new file mode 100644 (file)
index 0000000..a680533
--- /dev/null
@@ -0,0 +1,44 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestEventMachineQueue < Test::Unit::TestCase\r
+  def test_queue_push\r
+    s = 0\r
+    EM.run do\r
+      q = EM::Queue.new\r
+      q.push(1)\r
+      EM.next_tick { s = q.size; EM.stop }\r
+    end\r
+    assert_equal 1, s\r
+  end\r
+\r
+  def test_queue_pop\r
+    x,y,z = nil\r
+    EM.run do\r
+      q = EM::Queue.new\r
+      q.push(1,2,3)\r
+      q.pop { |v| x = v }\r
+      q.pop { |v| y = v }\r
+      q.pop { |v| z = v; EM.stop }\r
+    end\r
+    assert_equal 1, x\r
+    assert_equal 2, y\r
+    assert_equal 3, z\r
+  end\r
+\r
+  def test_queue_reactor_thread\r
+    q = EM::Queue.new\r
+\r
+    Thread.new { q.push(1,2,3) }.join\r
+    assert q.empty?\r
+    EM.run { EM.next_tick { EM.stop } }\r
+    assert_equal 3, q.size\r
+\r
+    x = nil\r
+    Thread.new { q.pop { |v| x = v } }.join\r
+    assert_equal nil, x\r
+    EM.run { EM.next_tick { EM.stop } }\r
+    assert_equal 1, x\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_running.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_running.rb
new file mode 100644 (file)
index 0000000..1e0b7e7
--- /dev/null
@@ -0,0 +1,42 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestRunning < Test::Unit::TestCase\r
+  def test_running\r
+    assert_equal( false, EM::reactor_running? )\r
+    r = false\r
+    EM.run {\r
+      r = EM::reactor_running?\r
+      EM.stop\r
+    }\r
+    assert_equal( true, r )\r
+  end\r
+end\r
+\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_sasl.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_sasl.rb
new file mode 100644 (file)
index 0000000..9b887d0
--- /dev/null
@@ -0,0 +1,72 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+\r
+class TestSASL < Test::Unit::TestCase\r
+\r
+  # SASL authentication is usually done with UNIX-domain sockets, but\r
+  # we'll use TCP so this test will work on Windows. As far as the\r
+  # protocol handlers are concerned, there's no difference.\r
+\r
+  Host,Port = "127.0.0.1",9560\r
+  TestUser,TestPsw = "someone", "password"\r
+\r
+  class SaslServer < EM::Connection\r
+    include EM::Protocols::SASLauth\r
+    def validate usr, psw, sys, realm\r
+      usr == TestUser and psw == TestPsw\r
+    end\r
+  end\r
+\r
+  class SaslClient < EM::Connection\r
+    include EM::Protocols::SASLauthclient\r
+  end\r
+\r
+  def test_sasl\r
+    resp = nil\r
+    EM.run {\r
+      EM.start_server( Host, Port, SaslServer )\r
+\r
+      c = EM.connect( Host, Port, SaslClient )\r
+      d = c.validate?( TestUser, TestPsw )\r
+      d.timeout 2\r
+      d.callback {\r
+        resp = true\r
+        EM.stop\r
+      }\r
+      d.errback {\r
+        resp = false\r
+        EM.stop\r
+      }\r
+    }\r
+    assert_equal( true, resp )\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_send_file.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_send_file.rb
new file mode 100644 (file)
index 0000000..8d51450
--- /dev/null
@@ -0,0 +1,242 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'socket'\r
+require 'test/unit'\r
+\r
+class TestSendFile < Test::Unit::TestCase\r
+\r
+  module TestModule\r
+    def post_init\r
+      send_file_data TestFilename\r
+      close_connection_after_writing\r
+    end\r
+  end\r
+\r
+  module TestClient\r
+    def data_to(&blk)\r
+      @data_to = blk\r
+    end\r
+    \r
+    def receive_data(data)\r
+      @data_to.call(data) if @data_to\r
+    end\r
+    \r
+    def unbind\r
+      EM.stop\r
+    end\r
+  end\r
+\r
+  TestHost = "0.0.0.0"\r
+  TestPort = 9055\r
+  TestFilename = "./xxxxxx"\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+    File.unlink( TestFilename ) if File.exist?( TestFilename )\r
+  end\r
+\r
+  def test_send_file\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 5000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, TestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    assert_equal( "A" * 5000, data )\r
+    File.unlink TestFilename\r
+  end\r
+\r
+  # EventMachine::Connection#send_file_data has a strict upper limit on the filesize it will work with.\r
+  def test_send_large_file\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 1000000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    ex_class = RUBY_PLATFORM == 'java' ? NativeException : RuntimeError\r
+    assert_raises( ex_class ) {\r
+      EM.run {\r
+        EM.start_server TestHost, TestPort, TestModule\r
+        EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+        EM.connect TestHost, TestPort, TestClient do |c|\r
+          c.data_to { |d| data << d }\r
+        end\r
+      }\r
+    }\r
+\r
+    File.unlink TestFilename\r
+  end\r
+\r
+\r
+  module StreamTestModule\r
+    def post_init\r
+      EM::Deferrable.future( stream_file_data(TestFilename)) {\r
+        close_connection_after_writing\r
+      }\r
+    end\r
+  end\r
+\r
+  module ChunkStreamTestModule\r
+    def post_init\r
+      EM::Deferrable.future( stream_file_data(TestFilename, :http_chunks=>true)) {\r
+        close_connection_after_writing\r
+      }\r
+    end\r
+  end\r
+\r
+  def test_stream_file_data\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 1000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, StreamTestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    assert_equal( "A" * 1000, data )\r
+\r
+    File.unlink TestFilename\r
+  end\r
+\r
+  def test_stream_chunked_file_data\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 1000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, ChunkStreamTestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    assert_equal( "3e8\r\n#{"A" * 1000}\r\n0\r\n\r\n", data )\r
+\r
+    File.unlink TestFilename\r
+  end\r
+\r
+  module BadFileTestModule\r
+    def post_init\r
+      de = stream_file_data( TestFilename+"..." )\r
+      de.errback {|msg|\r
+        send_data msg\r
+        close_connection_after_writing\r
+      }\r
+    end\r
+  end\r
+  def test_stream_bad_file\r
+    data = ''\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, BadFileTestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    assert_equal( "file not found", data )\r
+  end\r
+\r
+  def test_stream_large_file_data\r
+    begin\r
+      require 'fastfilereaderext'\r
+    rescue LoadError\r
+      return\r
+    end\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 10000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, StreamTestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    assert_equal( "A" * 10000, data )\r
+\r
+    File.unlink TestFilename\r
+  end\r
+\r
+  def test_stream_large_chunked_file_data\r
+    begin\r
+      require 'fastfilereaderext'\r
+    rescue LoadError\r
+      return\r
+    end\r
+    File.open( TestFilename, "w" ) {|f|\r
+      f << ("A" * 100000)\r
+    }\r
+\r
+    data = ''\r
+\r
+    EM.run {\r
+      EM.start_server TestHost, TestPort, ChunkStreamTestModule\r
+      EM.add_timer(2) {EM.stop} # avoid hanging in case of error\r
+      EM.connect TestHost, TestPort, TestClient do |c|\r
+        c.data_to { |d| data << d }\r
+      end\r
+    }\r
+\r
+    expected = [\r
+      "4000\r\n#{"A" * 16384}\r\n" * 6,\r
+      "6a0\r\n#{"A" * 0x6a0}\r\n",\r
+      "0\r\n\r\n"\r
+    ].join\r
+    assert_equal( expected, data )\r
+\r
+    File.unlink TestFilename\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_servers.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_servers.rb
new file mode 100644 (file)
index 0000000..5138d7f
--- /dev/null
@@ -0,0 +1,76 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+# \r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'socket'\r
+require 'test/unit'\r
+\r
+class TestServers < Test::Unit::TestCase\r
+\r
+  Host = "127.0.0.1"\r
+  Port = 9555\r
+\r
+  module NetstatHelper\r
+    GlobalUdp4Rexp = /udp.*\s+(?:\*|(?:0\.){3}0)[:.](\d+)\s/i\r
+    GlobalTcp4Rexp = /tcp.*\s+(?:\*|(?:0\.){3}0)[:.](\d+)\s/i\r
+    LocalUdpRexp = /udp.*\s+(?:127\.0\.0\.1|::1)[:.](\d+)\s/i\r
+    LocalTcpRexp = /tcp.*\s+(?:127\.0\.0\.1|::1)[:.](\d+)\s/i\r
+    def grep_netstat(pattern)\r
+      `netstat -an`.scan(/^.*$/).grep(pattern)\r
+    end\r
+  end\r
+  include NetstatHelper\r
+\r
+  class TestStopServer < EM::Connection\r
+    def initialize *args\r
+      super\r
+    end\r
+    def post_init\r
+      # TODO,sucks that this isn't OOPy enough.\r
+      EM.stop_server @server_instance\r
+    end\r
+  end\r
+\r
+  def run_test_stop_server\r
+    EM.run {\r
+      sig = EM.start_server(Host, Port)\r
+      assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).size >= 1, "Server didn't start")\r
+      EM.stop_server sig\r
+      # Give the server some time to shutdown.\r
+      EM.add_timer(0.1) {\r
+        assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).empty?, "Servers didn't stop")\r
+        EM.stop\r
+      }\r
+    }\r
+  end\r
+  def test_stop_server\r
+    assert(grep_netstat(LocalTcpRexp).grep(Port).empty?, "Port already in use")\r
+    5.times {run_test_stop_server}\r
+    assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).empty?, "Servers didn't stop")\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpclient.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpclient.rb
new file mode 100644 (file)
index 0000000..bb286cb
--- /dev/null
@@ -0,0 +1,83 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestSmtpClient < Test::Unit::TestCase\r
+\r
+  Localhost = "127.0.0.1"\r
+  Localport = 9801\r
+\r
+  def setup\r
+  end\r
+\r
+  def teardown\r
+  end\r
+\r
+  def test_a\r
+    # No real tests until we have a server implementation to test against.\r
+    # This is what the call looks like, though:\r
+    err = nil\r
+    EM.run {\r
+      d = EM::Protocols::SmtpClient.send :domain=>"example.com",\r
+      :host=>Localhost,\r
+      :port=>Localport, # optional, defaults 25\r
+      :starttls=>true,\r
+      :from=>"sender@example.com",\r
+      :to=> ["to_1@example.com", "to_2@example.com"],\r
+      :header=> {"Subject" => "This is a subject line"},\r
+      :body=> "This is the body of the email",\r
+      :verbose=>true\r
+      d.errback {|e|\r
+        err = e\r
+        EM.stop\r
+      }\r
+    }\r
+    assert(err)\r
+  end\r
+\r
+  def test_content\r
+    err = nil\r
+    EM.run {\r
+      d = EM::Protocols::SmtpClient.send :domain=>"example.com",\r
+      :host=>Localhost,\r
+      :port=>Localport, # optional, defaults 25\r
+      :starttls=>true,\r
+      :from=>"sender@example.com",\r
+      :to=> ["to_1@example.com", "to_2@example.com"],\r
+      :content => ["Subject: xxx\r\n\r\ndata\r\n.\r\n"],\r
+      :verbose=>true\r
+      d.errback {|e|\r
+        err = e\r
+        EM.stop\r
+      }\r
+    }\r
+    assert(err)\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpserver.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_smtpserver.rb
new file mode 100644 (file)
index 0000000..df5c876
--- /dev/null
@@ -0,0 +1,85 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestSmtpServer < Test::Unit::TestCase\r
+\r
+  # Don't test on port 25. It requires superuser and there's probably\r
+  # a mail server already running there anyway.\r
+  Localhost = "127.0.0.1"\r
+  Localport = 25001\r
+\r
+  # This class is an example of what you need to write in order\r
+  # to implement a mail server. You override the methods you are\r
+  # interested in. Some, but not all, of these are illustrated here.\r
+  #\r
+  class Mailserver < EM::Protocols::SmtpServer\r
+\r
+    attr_reader :my_msg_body, :my_sender, :my_recipients\r
+\r
+    def initialize *args\r
+      super\r
+    end\r
+    def receive_sender sender\r
+      @my_sender = sender\r
+      #p sender\r
+      true\r
+    end\r
+    def receive_recipient rcpt\r
+      @my_recipients ||= []\r
+      @my_recipients << rcpt\r
+      true\r
+    end\r
+    def receive_data_chunk c\r
+      @my_msg_body = c.last\r
+    end\r
+    def connection_ended\r
+      EM.stop\r
+    end\r
+  end\r
+\r
+  def test_mail\r
+    c = nil\r
+    EM.run {\r
+      EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn}\r
+      EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error\r
+      EM::Protocols::SmtpClient.send :host=>Localhost,\r
+        :port=>Localport,\r
+        :domain=>"bogus",\r
+        :from=>"me@example.com",\r
+        :to=>"you@example.com",\r
+        :header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"},\r
+        :body=>"Not much of interest here."\r
+\r
+    }\r
+    assert_equal( "Not much of interest here.", c.my_msg_body )\r
+    assert_equal( "<me@example.com>", c.my_sender )\r
+    assert_equal( ["<you@example.com>"], c.my_recipients )\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_spawn.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_spawn.rb
new file mode 100644 (file)
index 0000000..d9614c5
--- /dev/null
@@ -0,0 +1,322 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 25 Aug 2007\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+#\r
+\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+\r
+\r
+class TestSpawn < Test::Unit::TestCase\r
+\r
+  # Spawn a process that simply stops the reactor.\r
+  # Assert that the notification runs after the block that calls it.\r
+  #\r
+  def test_stop\r
+    x = nil\r
+    EM.run {\r
+      s = EM.spawn {EM.stop}\r
+      s.notify\r
+      x = true\r
+    }\r
+    assert x\r
+  end\r
+\r
+\r
+  # Pass a parameter to a spawned process.\r
+  #\r
+  def test_parms\r
+    val = 5\r
+    EM.run {\r
+      s = EM.spawn {|v| val *= v; EM.stop}\r
+      s.notify 3\r
+    }\r
+    assert_equal( 15, val )\r
+  end\r
+\r
+  # Pass multiple parameters to a spawned process.\r
+  #\r
+  def test_multiparms\r
+    val = 5\r
+    EM.run {\r
+      s = EM.spawn {|v1,v2| val *= (v1 + v2); EM.stop}\r
+      s.notify 3,4\r
+    }\r
+    assert_equal( 35, val )\r
+  end\r
+\r
+\r
+  # This test demonstrates that a notification does not happen immediately,\r
+  # but rather is scheduled sometime after the current code path completes.\r
+  #\r
+  def test_race\r
+    x = 0\r
+    EM.run {\r
+      s = EM.spawn {x *= 2; EM.stop}\r
+      s.notify\r
+      x = 2\r
+    }\r
+    assert_equal( 4, x)\r
+  end\r
+\r
+\r
+  # Spawn a process and notify it 25 times to run fibonacci\r
+  # on a pair of global variables.\r
+  #\r
+  def test_fibonacci\r
+    x = 1\r
+    y = 1\r
+    EM.run {\r
+      s = EM.spawn {x,y = y,x+y}\r
+      25.times {s.notify}\r
+\r
+      t = EM.spawn {EM.stop}\r
+      t.notify\r
+    }\r
+    assert_equal( 121393, x)\r
+    assert_equal( 196418, y)\r
+  end\r
+\r
+  # This one spawns 25 distinct processes, and notifies each one once,\r
+  # rather than notifying a single process 25 times.\r
+  #\r
+  def test_another_fibonacci\r
+    x = 1\r
+    y = 1\r
+    EM.run {\r
+      25.times {\r
+      s = EM.spawn {x,y = y,x+y}\r
+      s.notify\r
+    }\r
+\r
+    t = EM.spawn {EM.stop}\r
+    t.notify\r
+    }\r
+    assert_equal( 121393, x)\r
+    assert_equal( 196418, y)\r
+  end\r
+\r
+\r
+  # Make a chain of processes that notify each other in turn\r
+  # with intermediate fibonacci results. The final process in\r
+  # the chain stops the loop and returns the result.\r
+  #\r
+  def test_fibonacci_chain\r
+    a,b = nil\r
+\r
+    EM.run {\r
+      nextpid = EM.spawn {|x,y|\r
+        a,b = x,y\r
+        EM.stop\r
+      }\r
+\r
+      25.times {\r
+        n = nextpid\r
+        nextpid = EM.spawn {|x,y| n.notify( y, x+y )}\r
+      }\r
+\r
+      nextpid.notify( 1, 1 )\r
+    }\r
+\r
+    assert_equal( 121393, a)\r
+    assert_equal( 196418, b)\r
+  end\r
+\r
+\r
+  # EM#yield gives a spawed process to yield control to other processes\r
+  # (in other words, to stop running), and to specify a different code block\r
+  # that will run on its next notification.\r
+  #\r
+  def test_yield\r
+    a = 0\r
+    EM.run {\r
+      n = EM.spawn {\r
+        a += 10\r
+        EM.yield {\r
+          a += 20\r
+          EM.yield {\r
+            a += 30\r
+            EM.stop\r
+          }\r
+        }\r
+      }\r
+      n.notify\r
+      n.notify\r
+      n.notify\r
+    }\r
+    assert_equal( 60, a )\r
+  end\r
+\r
+  # EM#yield_and_notify behaves like EM#yield, except that it also notifies the\r
+  # yielding process. This may sound trivial, since the yield block will run very\r
+  # shortly after with no action by the program, but this actually can be very useful,\r
+  # because it causes the reactor core to execute once before the yielding process\r
+  # gets control back. So it can be used to allow heavily-used network connections\r
+  # to clear buffers, or allow other processes to process their notifications.\r
+  #\r
+  # Notice in this test code that only a simple notify is needed at the bottom\r
+  # of the initial block. Even so, all of the yielded blocks will execute.\r
+  #\r
+  def test_yield_and_notify\r
+    a = 0\r
+    EM.run {\r
+      n = EM.spawn {\r
+        a += 10\r
+        EM.yield_and_notify {\r
+          a += 20\r
+          EM.yield_and_notify {\r
+            a += 30\r
+            EM.stop\r
+          }\r
+        }\r
+      }\r
+      n.notify\r
+    }\r
+    assert_equal( 60, a )\r
+  end\r
+\r
+  # resume is an alias for notify.\r
+  #\r
+  def test_resume\r
+    EM.run {\r
+      n = EM.spawn {EM.stop}\r
+      n.resume\r
+    }\r
+    assert true\r
+  end\r
+\r
+  # run is an idiomatic alias for notify.\r
+  #\r
+  def test_run\r
+    EM.run {\r
+      (EM.spawn {EM.stop}).run\r
+    }\r
+    assert true\r
+  end\r
+\r
+\r
+  # Clones the ping-pong example from the Erlang tutorial, in much less code.\r
+  # Illustrates that a spawned block executes in the context of a SpawnableObject.\r
+  # (Meaning, we can pass self as a parameter to another process that can then\r
+  # notify us.)\r
+  #\r
+  def test_ping_pong\r
+    n_pongs = 0\r
+    EM.run {\r
+      pong = EM.spawn {|x, ping|\r
+        n_pongs += 1\r
+        ping.notify( x-1 )\r
+      }\r
+      ping = EM.spawn {|x|\r
+        if x > 0\r
+          pong.notify x, self\r
+        else\r
+          EM.stop\r
+        end\r
+      }\r
+      ping.notify 3\r
+    }\r
+    assert_equal( 3, n_pongs )\r
+  end\r
+\r
+  # Illustrates that you can call notify inside a notification, and it will cause\r
+  # the currently-executing process to be re-notified. Of course, the new notification\r
+  # won't run until sometime after the current one completes.\r
+  #\r
+  def test_self_notify\r
+    n = 0\r
+    EM.run {\r
+      pid = EM.spawn {|x|\r
+        if x > 0\r
+          n += x\r
+          notify( x-1 )\r
+        else\r
+          EM.stop\r
+        end\r
+      }\r
+      pid.notify 3\r
+    }\r
+    assert_equal( 6, n )\r
+  end\r
+\r
+\r
+  # Illustrates that the block passed to #spawn executes in the context of a\r
+  # SpawnedProcess object, NOT in the local context. This can often be deceptive.\r
+  #\r
+  class BlockScopeTest\r
+    attr_reader :var\r
+    def run\r
+      # The following line correctly raises a NameError.\r
+      # The problem is that the programmer expected the spawned block to\r
+      # execute in the local context, but it doesn't.\r
+      #\r
+      # (EM.spawn { do_something }).notify ### NO! BAD!\r
+\r
+\r
+\r
+      # The following line correctly passes self as a parameter to the\r
+      # notified process.\r
+      #\r
+      (EM.spawn {|obj| obj.do_something }).notify(self)\r
+\r
+\r
+\r
+      # Here's another way to do it. This works because "myself" is bound\r
+      # in the local scope, unlike "self," so the spawned block sees it.\r
+      #\r
+      myself = self\r
+      (EM.spawn { myself.do_something }).notify\r
+\r
+\r
+\r
+      # And we end the loop.\r
+      # This is a tangential point, but observe that #notify never blocks.\r
+      # It merely appends a message to the internal queue of a spawned process\r
+      # and returns. As it turns out, the reactor processes notifications for ALL\r
+      # spawned processes in the order that #notify is called. So there is a\r
+      # reasonable expectation that the process which stops the reactor will\r
+      # execute after the previous ones in this method. HOWEVER, this is NOT\r
+      # a documented behavior and is subject to change.\r
+      #\r
+      (EM.spawn {EM.stop}).notify\r
+    end\r
+    def do_something\r
+      @var ||= 0\r
+      @var += 100\r
+    end\r
+  end\r
+\r
+  def test_block_scope\r
+    bs = BlockScopeTest.new\r
+    EM.run {\r
+      bs.run\r
+    }\r
+    assert_equal( 200, bs.var )\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_args.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_args.rb
new file mode 100644 (file)
index 0000000..13458bb
--- /dev/null
@@ -0,0 +1,79 @@
+require "test/unit"
+require 'tempfile'
+
+$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
+require "eventmachine"
+
+module EventMachine
+  def self._set_mocks
+    class <<self
+      alias set_tls_parms_old set_tls_parms
+      alias start_tls_old start_tls
+      begin
+        old, $VERBOSE = $VERBOSE, nil
+        def set_tls_parms *args; end
+        def start_tls *args; end
+      ensure
+        $VERBOSE = old
+      end
+    end
+  end
+
+  def self._clear_mocks
+    class <<self
+      begin
+        old, $VERBOSE = $VERBOSE, nil
+        alias set_tls_parms set_tls_parms_old
+        alias start_tls start_tls_old
+      ensure
+        $VERBOSE = old
+      end
+    end
+  end
+end
+
+  
+
+class TestSslArgs < Test::Unit::TestCase
+  def setup
+    EventMachine._set_mocks
+  end
+  
+  def teardown
+    EventMachine._clear_mocks
+  end
+  
+  def test_tls_params_file_doesnt_exist
+    priv_file, cert_file = 'foo_priv_key', 'bar_cert_file'
+    [priv_file, cert_file].all? do |f|
+      assert(!File.exists?(f), "Cert file #{f} seems to exist, and should not for the tests")
+    end
+    
+    # associate_callback_target is a pain! (build!)
+    conn = EventMachine::Connection.new('foo')
+    
+    assert_raises(EventMachine::FileNotFoundException) do
+      conn.start_tls(:private_key_file => priv_file)
+    end
+    assert_raises(EventMachine::FileNotFoundException) do
+      conn.start_tls(:cert_chain_file => cert_file)
+    end
+    assert_raises(EventMachine::FileNotFoundException) do
+      conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file)
+    end
+  end
+  
+  def test_tls_params_file_does_exist
+    priv_file = Tempfile.new('em_test')
+    cert_file = Tempfile.new('em_test')
+    priv_file_path = priv_file.path
+    cert_file_path = cert_file.path
+    conn = EventMachine::Connection.new('foo')
+    params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path}
+    begin
+      conn.start_tls params
+    rescue Object
+      assert(false, 'should not have raised an exception')
+    end
+  end
+end if EM.ssl?
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_methods.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_methods.rb
new file mode 100644 (file)
index 0000000..3688954
--- /dev/null
@@ -0,0 +1,50 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestSSLMethods < Test::Unit::TestCase\r
+\r
+  module ServerHandler\r
+\r
+    def post_init\r
+      start_tls\r
+    end\r
+\r
+    def ssl_handshake_completed\r
+      $server_called_back = true\r
+      $server_cert_value = get_peer_cert\r
+    end\r
+\r
+  end\r
+\r
+  module ClientHandler\r
+\r
+    def post_init\r
+      start_tls\r
+    end\r
+\r
+    def ssl_handshake_completed\r
+      $client_called_back = true\r
+      $client_cert_value = get_peer_cert\r
+      EM.stop_event_loop\r
+    end\r
+\r
+  end\r
+\r
+  def test_ssl_methods\r
+    $server_called_back, $client_called_back = false, false\r
+    $server_cert_value, $client_cert_value = nil, nil\r
+\r
+    EM.run {\r
+      EM.start_server("127.0.0.1", 9999, ServerHandler)\r
+      EM.connect("127.0.0.1", 9999, ClientHandler)\r
+    }\r
+\r
+    assert($server_called_back)\r
+    assert($client_called_back)\r
+\r
+    assert($server_cert_value.is_a?(NilClass))\r
+    assert($client_cert_value.is_a?(String))\r
+  end\r
+\r
+end if EM.ssl?
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_verify.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ssl_verify.rb
new file mode 100644 (file)
index 0000000..ed92380
--- /dev/null
@@ -0,0 +1,82 @@
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestSslVerify < Test::Unit::TestCase\r
+\r
+  def setup\r
+    $dir = File.dirname(File.expand_path(__FILE__)) + '/'\r
+    $cert_from_file = File.read($dir+'client.crt')\r
+  end\r
+\r
+  module Client\r
+    def connection_completed\r
+      start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')\r
+    end\r
+\r
+    def ssl_handshake_completed\r
+      $client_handshake_completed = true\r
+      close_connection\r
+    end\r
+\r
+    def unbind\r
+      EM.stop_event_loop\r
+    end\r
+  end\r
+\r
+  module AcceptServer\r
+    def post_init\r
+      start_tls(:verify_peer => true)\r
+    end\r
+\r
+    def ssl_verify_peer(cert)\r
+      $cert_from_server = cert\r
+      true\r
+    end\r
+\r
+    def ssl_handshake_completed\r
+      $server_handshake_completed = true\r
+    end\r
+  end\r
+\r
+  module DenyServer\r
+    def post_init\r
+      start_tls(:verify_peer => true)\r
+    end\r
+\r
+    def ssl_verify_peer(cert)\r
+      $cert_from_server = cert\r
+      # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed.\r
+      false\r
+    end\r
+\r
+    def ssl_handshake_completed\r
+      $server_handshake_completed = true\r
+    end\r
+  end\r
+\r
+  def test_accept_server\r
+    $client_handshake_completed, $server_handshake_completed = false, false\r
+    EM.run {\r
+      EM.start_server("127.0.0.1", 16784, AcceptServer)\r
+      EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature")\r
+    }\r
+\r
+    assert_equal($cert_from_file, $cert_from_server)\r
+    assert($client_handshake_completed)\r
+    assert($server_handshake_completed)\r
+  end\r
+\r
+  def test_deny_server\r
+    $client_handshake_completed, $server_handshake_completed = false, false\r
+    EM.run {\r
+      EM.start_server("127.0.0.1", 16784, DenyServer)\r
+      EM.connect("127.0.0.1", 16784, Client)\r
+    }\r
+\r
+    assert_equal($cert_from_file, $cert_from_server)\r
+    assert(!$client_handshake_completed)\r
+    assert(!$server_handshake_completed)\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_timers.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_timers.rb
new file mode 100644 (file)
index 0000000..cc0dc5a
--- /dev/null
@@ -0,0 +1,162 @@
+# $Id$
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage::  http://rubyeventmachine.com
+# Date:: 8 April 2006
+# 
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+# 
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+#
+
+$:.unshift "../lib"
+require 'eventmachine'
+require 'test/unit'
+
+class TestTimers < Test::Unit::TestCase
+
+  def test_timer_with_block
+    x = false
+    EventMachine.run {
+      EventMachine::Timer.new(0.25) {
+        x = true
+        EventMachine.stop
+      }
+    }
+    assert x
+  end
+
+  def test_timer_with_proc
+    x = false
+    EventMachine.run {
+      EventMachine::Timer.new(0.25, proc {
+        x = true
+        EventMachine.stop
+      })
+    }
+    assert x
+  end
+
+  def test_timer_cancel
+    x = true
+    EventMachine.run {
+      timer = EventMachine::Timer.new(0.25, proc { x = false })
+      timer.cancel
+      EventMachine::Timer.new(0.5, proc {EventMachine.stop})
+    }
+    assert x
+  end
+
+  def test_periodic_timer
+    x = 0
+    EventMachine.run {
+      EventMachine::PeriodicTimer.new(0.1) do
+        x += 1
+        EventMachine.stop if x == 4
+      end
+    }
+    assert( x == 4 )
+  end
+
+  def test_add_periodic_timer
+    x = 0
+    EM.run {
+      t = EM.add_periodic_timer(0.1) do
+        x += 1
+        EM.stop  if x == 4
+      end
+      assert t.respond_to?(:cancel)
+    }
+  end
+
+  def test_periodic_timer_cancel
+    x = 0
+    EventMachine.run {
+      pt = EventMachine::PeriodicTimer.new(0.25, proc { x += 1 })
+      pt.cancel
+      EventMachine::Timer.new(0.5) {EventMachine.stop}
+    }
+    assert( x == 0 )
+  end
+
+  def test_add_periodic_timer_cancel
+    x = 0
+    EventMachine.run {
+      pt = EM.add_periodic_timer(0.25) { x += 1 }
+      EM.cancel_timer(pt)
+      EM.add_timer(0.5) { EM.stop }
+    }
+    assert( x == 0 )
+  end
+
+  def test_periodic_timer_self_cancel
+    x = 0
+    EventMachine.run {
+      pt = EventMachine::PeriodicTimer.new(0.1) {
+        x += 1
+        if x == 4
+          pt.cancel
+          EventMachine.stop
+        end
+      }
+    }
+    assert( x == 4 )
+  end
+
+
+  # This test is only applicable to compiled versions of the reactor.
+  # Pure ruby and java versions have no built-in limit on the number of outstanding timers.
+  #
+  def test_timer_change_max_outstanding
+    ten_thousand_timers = proc {
+      10001.times {
+        EM.add_timer(5) {}
+      }
+    }
+
+    EM.run {
+      if EM.library_type == :pure_ruby
+        ten_thousand_timers.call
+      elsif EM.library_type == :java
+        ten_thousand_timers.call
+      else
+        begin
+          assert_raises( RuntimeError ) {
+            ten_thousand_timers.call
+          }
+        rescue Object
+          p $!
+          assert(false, $!.message)
+        end
+      end
+      EM.stop
+    }
+
+    assert(!EM.reactor_running?, 'Reactor running when it should not be.')
+    assert( EM.get_max_timers != 10001 )
+    EM.set_max_timers( 10001 )
+    assert( EM.get_max_timers == 10001 )
+
+    EM.run {
+      ten_thousand_timers.call
+      EM.stop
+    }
+  end
+
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ud.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/test_ud.rb
new file mode 100644 (file)
index 0000000..1d72072
--- /dev/null
@@ -0,0 +1,36 @@
+# $Id$\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage::  http://rubyeventmachine.com\r
+# Date:: 8 April 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+#\r
+\r
+$:.unshift "../lib"\r
+require 'eventmachine'\r
+require 'test/unit'\r
+\r
+class TestUserDefinedEvents < Test::Unit::TestCase\r
+\r
+  def test_a\r
+  end\r
+\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/testem.rb b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/tests/testem.rb
new file mode 100644 (file)
index 0000000..d4fb7c5
--- /dev/null
@@ -0,0 +1,31 @@
+# $Id$\r
+\r
+require 'test/unit'\r
+\r
+module EmTestRunner\r
+  @em_root = File.expand_path(File.dirname(__FILE__) + '/../')\r
+  @lib_dir = File.join(@em_root, 'lib')\r
+  @ext_dir = File.join(@em_root, 'ext')\r
+  @java_dir = File.join(@em_root, 'java')\r
+\r
+  def self.run(glob = 'test_*.rb')\r
+    $:.unshift(@lib_dir)\r
+    $:.unshift(@ext_dir)\r
+    $:.unshift(@java_dir)\r
+\r
+    case glob\r
+    when Array\r
+      files = glob\r
+    else\r
+      files = Dir[File.dirname(__FILE__) + '/' + glob]\r
+    end\r
+\r
+    files.each do |tc|\r
+      require tc\r
+    end\r
+  end\r
+end\r
+\r
+if __FILE__ == $0\r
+  EmTestRunner.run\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/web/whatis b/ruby/lib/ruby/gems/1.8/gems/eventmachine-0.12.10-x86-mswin32-60/web/whatis
new file mode 100644 (file)
index 0000000..b9973e5
--- /dev/null
@@ -0,0 +1,7 @@
+EventMachine is a library for Ruby, C++, and Java programs. It provides event-driven I/O using the Reactor pattern.\r
+\r
+EventMachine is designed to simultaneously meet two key needs:\r
+- Extremely high scalability, performance and stability for the most demanding production environments; and\r
+- An API that <i>eliminates</i> the complexities of high-performance threaded network programming, allowing engineers to concentrate on their application logic.\r
+\r
+This unique combination makes EventMachine a premier choice for designers of critical networked applications, including web servers and proxies, email and IM production systems, authentication/authorization processors, and many more.\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/.require_paths b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/.require_paths
deleted file mode 100644 (file)
index 0eaa334..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-lib\r
-ext\r
-bin\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/CHANGELOG b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/CHANGELOG
deleted file mode 100644 (file)
index 637fe89..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-
-v1.1.5. Fix bug where num_processors is not actually set from mongrel_rails.
-
-v1.1.4. Fix camping handler. Correct treatment of @throttle parameter.
-
-v1.1.3. Fix security flaw of DirHandler; reported on mailing list.
-
-v1.1.2. Fix worker termination bug; fix JRuby 1.0.3 load order issue; fix require issue on systems without Rubygems.
-
-v1.1.1. Fix mongrel_rails restart bug; fix bug with Rack status codes.
-
-v1.1. Pure Ruby URIClassifier. More modular architecture. JRuby support. Move C URIClassifier into mongrel_experimental project.
-
-v1.0.4. Backport fixes for versioning inconsistency, mongrel_rails bug, and DirHandler bug.
-
-v1.0.3. Fix user-switching bug; make people upgrade to the latest from the RC.
-
-v1.0.2. Signed gem; many minor bugfixes and patches.
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/COPYING b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/COPYING
deleted file mode 100644 (file)
index e7ada7d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
-<zedshaw at zedshaw dot com> You can redistribute it and/or modify it under
-either the terms of the GPL or the conditions below:
-
-1. You may make and give away verbatim copies of the source form of the
-   software without restriction, provided that you duplicate all of the
-   original copyright notices and associated disclaimers.
-
-2. You may modify your copy of the software in any way, provided that
-   you do at least ONE of the following:
-
-     a) place your modifications in the Public Domain or otherwise make them
-     Freely Available, such as by posting said modifications to Usenet or an
-     equivalent medium, or by allowing the author to include your
-     modifications in the software.
-
-     b) use the modified software only within your corporation or
-        organization.
-
-     c) rename any non-standard executables so the names do not conflict with
-     standard executables, which must also be provided.
-
-     d) make other distribution arrangements with the author.
-
-3. You may distribute the software in object code or executable
-   form, provided that you do at least ONE of the following:
-
-     a) distribute the executables and library files of the software,
-     together with instructions (in the manual page or equivalent) on where
-     to get the original distribution.
-
-     b) accompany the distribution with the machine-readable source of the
-     software.
-
-     c) give non-standard executables non-standard names, with
-        instructions on where to get the original software distribution.
-
-     d) make other distribution arrangements with the author.
-
-4. You may modify and include the part of the software into any other
-   software (possibly commercial).  But some files in the distribution
-   are not written by the author, so that they are not under this terms.
-
-5. The scripts and library files supplied as input to or produced as 
-   output from the software do not automatically fall under the
-   copyright of the software, but belong to whomever generated them, 
-   and may be sold commercially, and may be aggregated with this
-   software.
-
-6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-   PURPOSE.
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/LICENSE b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/LICENSE
deleted file mode 100644 (file)
index e5a926e..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
-<zedshaw at zedshaw dot com> and contributors. You can redistribute it 
-and/or modify it under either the terms of the GPL2 or the conditions below:
-
-1. You may make and give away verbatim copies of the source form of the
-   software without restriction, provided that you duplicate all of the
-   original copyright notices and associated disclaimers.
-
-2. You may modify your copy of the software in any way, provided that
-   you do at least ONE of the following:
-
-     a) place your modifications in the Public Domain or otherwise make them
-     Freely Available, such as by posting said modifications to Usenet or an
-     equivalent medium, or by allowing the author to include your
-     modifications in the software.
-
-     b) use the modified software only within your corporation or
-        organization.
-
-     c) rename any non-standard executables so the names do not conflict with
-     standard executables, which must also be provided.
-
-     d) make other distribution arrangements with the author.
-
-3. You may distribute the software in object code or executable
-   form, provided that you do at least ONE of the following:
-
-     a) distribute the executables and library files of the software,
-     together with instructions (in the manual page or equivalent) on where
-     to get the original distribution.
-
-     b) accompany the distribution with the machine-readable source of the
-     software.
-
-     c) give non-standard executables non-standard names, with
-        instructions on where to get the original software distribution.
-
-     d) make other distribution arrangements with the author.
-
-4. You may modify and include the part of the software into any other
-   software (possibly commercial).  But some files in the distribution
-   are not written by the author, so that they are not under this terms.
-
-5. The scripts and library files supplied as input to or produced as 
-   output from the software do not automatically fall under the
-   copyright of the software, but belong to whomever generated them, 
-   and may be sold commercially, and may be aggregated with this
-   software.
-
-6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-   PURPOSE.
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/Manifest b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/Manifest
deleted file mode 100644 (file)
index 6a77dad..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-bin/mongrel_rails
-CHANGELOG
-COPYING
-examples/builder.rb
-examples/camping/blog.rb
-examples/camping/README
-examples/camping/tepee.rb
-examples/httpd.conf
-examples/mime.yaml
-examples/mongrel.conf
-examples/mongrel_simple_ctrl.rb
-examples/mongrel_simple_service.rb
-examples/monitrc
-examples/random_thrash.rb
-examples/simpletest.rb
-examples/webrick_compare.rb
-ext/http11/ext_help.h
-ext/http11/extconf.rb
-ext/http11/http11.c
-ext/http11/http11_parser.c
-ext/http11/http11_parser.h
-ext/http11/http11_parser.java.rl
-ext/http11/http11_parser.rl
-ext/http11/http11_parser_common.rl
-ext/http11_java/Http11Service.java
-ext/http11_java/org/jruby/mongrel/Http11.java
-ext/http11_java/org/jruby/mongrel/Http11Parser.java
-lib/mongrel/camping.rb
-lib/mongrel/cgi.rb
-lib/mongrel/command.rb
-lib/mongrel/configurator.rb
-lib/mongrel/const.rb
-lib/mongrel/debug.rb
-lib/mongrel/gems.rb
-lib/mongrel/handlers.rb
-lib/mongrel/header_out.rb
-lib/mongrel/http_request.rb
-lib/mongrel/http_response.rb
-lib/mongrel/init.rb
-lib/mongrel/mime_types.yml
-lib/mongrel/rails.rb
-lib/mongrel/stats.rb
-lib/mongrel/tcphack.rb
-lib/mongrel/uri_classifier.rb
-lib/mongrel.rb
-LICENSE
-Manifest
-mongrel-public_cert.pem
-mongrel.gemspec
-README
-setup.rb
-test/mime.yaml
-test/mongrel.conf
-test/test_cgi_wrapper.rb
-test/test_command.rb
-test/test_conditional.rb
-test/test_configurator.rb
-test/test_debug.rb
-test/test_handlers.rb
-test/test_http11.rb
-test/test_redirect_handler.rb
-test/test_request_progress.rb
-test/test_response.rb
-test/test_stats.rb
-test/test_uriclassifier.rb
-test/test_ws.rb
-test/testhelp.rb
-TODO
-tools/trickletest.rb
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/README b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/README
deleted file mode 100644 (file)
index 816fa32..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-= Mongrel:  Simple Fast Mostly Ruby Web Server
-
-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.
-
-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.
-
-See http://mongrel.rubyforge.org for more information.
-
-== License
-
-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.
-
-== Quick Start
-
-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:
-
- $ gem install mongrel
-
-Now you should have the mongrel_rails command available in your PATH, so just do the following:
-
- $ cd myrailsapp
- $ mongrel_rails start
-
-This will start it in the foreground so you can play with it.  It runs your application in production mode.  To get help do:
-
- $ mongrel_rails start -h 
-
-Finally, you can then start in background mode:
-
- $ mongrel_rails start -d
-
-And you can stop it whenever you like with:
-
- $ mongrel_rails stop
-
-All of which should be done from your application's directory.  It writes the PID of the process you ran into log/mongrel.pid.
-
-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.
-
-== Install
-
-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.
-
-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.
-
-Finally, the source includes a setup.rb for those who hate RubyGems.
-
-== Usage
-
-The examples/simpletest.rb file has the following code as the simplest example:
-
- require 'mongrel'
-
- class SimpleHandler < Mongrel::HttpHandler
-    def process(request, response)
-      response.start(200) do |head,out|
-        head["Content-Type"] = "text/plain"
-        out.write("hello!\n")
-      end
-    end
- end
-
- h = Mongrel::HttpServer.new("0.0.0.0", "3000")
- h.register("/test", SimpleHandler.new)
- h.register("/files", Mongrel::DirHandler.new("."))
- h.run.join
-
-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.
-
-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.*
-
-== Contact
-
-E-mail the Mongrel list at http://rubyforge.org/mailman/listinfo/mongrel-users and someone will help you. Comments about the API are welcome.
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/TODO b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/TODO
deleted file mode 100644 (file)
index 3e32555..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-v1.2. Rewrite and merge mongrel cluster and mongrel_rails into something small and maintainable. Remove gem_plugin entirely.
-
-v1.1.1. See if Java is setting the server version string in the request properly.
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/bin/mongrel_rails b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/bin/mongrel_rails
deleted file mode 100644 (file)
index 51264e4..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
-# for more information.
-
-require 'yaml'
-require 'etc'
-
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
-require 'mongrel'
-require 'mongrel/rails'
-
-Mongrel::Gems.require 'gem_plugin'
-
-# require 'ruby-debug'
-# Debugger.start
-
-module Mongrel
-  class Start < GemPlugin::Plugin "/commands"
-    include Mongrel::Command::Base
-
-    def configure
-      options [
-        ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
-        ["-d", "--daemonize", "Run daemonized in the background", :@daemon, false],
-        ['-p', '--port PORT', "Which port to bind to", :@port, 3000],
-        ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
-        ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
-        ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
-        ['-n', '--num-processors INT', "Number of processors active before clients denied", :@num_processors, 1024],
-        ['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, 60],
-        ['-t', '--throttle TIME', "Time to pause (in hundredths of a second) between accepting clients", :@throttle, 0],
-        ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
-        ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
-        ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
-        ['-B', '--debug', "Enable debugging mode", :@debug, false],
-        ['-C', '--config PATH', "Use a config file", :@config_file, nil],
-        ['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil],
-        ['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil],
-        ['', '--user USER', "User to run as", :@user, nil],
-        ['', '--group GROUP', "Group to run as", :@group, nil],
-        ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil]
-      ]
-    end
-
-    def validate
-      if @config_file
-        valid_exists?(@config_file, "Config file not there: #@config_file")
-        return false unless @valid
-        @config_file = File.expand_path(@config_file)
-        load_config
-        return false unless @valid
-      end
-
-      @cwd = File.expand_path(@cwd)
-      valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
-
-      # Change there to start, then we'll have to come back after daemonize
-      Dir.chdir(@cwd)
-
-      valid?(@prefix[0] == ?/ && @prefix[-1] != ?/, "Prefix must begin with / and not end in /") if @prefix
-      valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
-      valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
-      valid_dir? @docroot, "Path to docroot not valid: #@docroot"
-      valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
-      valid_exists? @config_file, "Config file not there: #@config_file" if @config_file
-      valid_dir? File.dirname(@generate), "Problem accessing directory to #@generate" if @generate
-      valid_user? @user if @user
-      valid_group? @group if @group
-
-      return @valid
-    end
-
-    def run
-      if @generate
-       @generate = File.expand_path(@generate)
-        STDERR.puts "** Writing config to \"#@generate\"."
-        open(@generate, "w") {|f| f.write(settings.to_yaml) }
-        STDERR.puts "** Finished.  Run \"mongrel_rails start -C #@generate\" to use the config file."
-        exit 0
-      end
-
-      config = Mongrel::Rails::RailsConfigurator.new(settings) do
-        if defaults[:daemon]
-          if File.exist? defaults[:pid_file]
-            log "!!! PID file #{defaults[:pid_file]} already exists.  Mongrel could be running already.  Check your #{defaults[:log_file]} for errors."
-            log "!!! Exiting with error.  You must stop mongrel and clear the .pid before I'll attempt a start."
-            exit 1
-          end
-
-          daemonize
-          log "Daemonized, any open files are closed.  Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info."
-          log "Settings loaded from #{@config_file} (they override command line)." if @config_file
-        end
-
-        log "Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}"
-
-        listener do
-          mime = {}
-          if defaults[:mime_map]
-            log "Loading additional MIME types from #{defaults[:mime_map]}"
-            mime = load_mime_map(defaults[:mime_map], mime)
-          end
-
-          if defaults[:debug]
-            log "Installing debugging prefixed filters. Look in log/mongrel_debug for the files."
-            debug "/"
-          end
-
-          if defaults[:config_script]
-            log "Loading #{defaults[:config_script]} external config script"
-            run_config(defaults[:config_script])
-          end
-
-          log "Starting Rails with #{defaults[:environment]} environment..."
-          log "Mounting Rails at #{defaults[:prefix]}..." if defaults[:prefix]
-          uri defaults[:prefix] || "/", :handler => rails(:mime => mime, :prefix => defaults[:prefix])
-          log "Rails loaded."
-
-          log "Loading any Rails specific GemPlugins"
-          load_plugins
-
-          setup_rails_signals
-        end
-      end
-
-      config.run
-      config.log "Mongrel #{Mongrel::Const::MONGREL_VERSION} available at #{@address}:#{@port}"
-
-      if config.defaults[:daemon]
-        config.write_pid_file
-      else
-        config.log "Use CTRL-C to stop." 
-      end
-
-      config.join
-
-      if config.needs_restart
-        if RUBY_PLATFORM !~ /mswin/
-          cmd = "ruby #{__FILE__} start #{original_args.join(' ')}"
-          config.log "Restarting with arguments:  #{cmd}"
-          config.stop(false, true)
-          config.remove_pid_file
-
-          if config.defaults[:daemon]
-            system cmd
-          else
-            STDERR.puts "Can't restart unless in daemon mode."
-            exit 1
-          end
-        else
-          config.log "Win32 does not support restarts. Exiting."
-        end
-      end
-    end
-
-    def load_config
-      settings = {}
-      begin
-        settings = YAML.load_file(@config_file)
-      ensure
-        STDERR.puts "** Loading settings from #{@config_file} (they override command line)." unless @daemon || settings[:daemon] 
-      end
-
-      settings[:includes] ||= ["mongrel"]
-
-      # Config file settings will override command line settings
-      settings.each do |key, value|
-        key = key.to_s
-        if config_keys.include?(key)
-          key = 'address' if key == 'host'
-          self.instance_variable_set("@#{key}", value)
-        else
-          failure "Unknown configuration setting: #{key}"  
-          @valid = false
-        end
-      end
-    end
-
-    def config_keys
-      @config_keys ||=
-        %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix)
-    end
-
-    def settings
-      config_keys.inject({}) do |hash, key|
-        value = self.instance_variable_get("@#{key}")
-        key = 'host' if key == 'address'
-        hash[key.to_sym] ||= value
-        hash
-      end
-    end
-  end
-
-  def Mongrel::send_signal(signal, pid_file)
-    pid = open(pid_file).read.to_i
-    print "Sending #{signal} to Mongrel at PID #{pid}..."
-    begin
-      Process.kill(signal, pid)
-    rescue Errno::ESRCH
-      puts "Process does not exist.  Not running."
-    end
-
-    puts "Done."
-  end
-
-
-  class Stop < GemPlugin::Plugin "/commands"
-    include Mongrel::Command::Base
-
-    def configure 
-      options [ 
-        ['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."],
-        ['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
-        ['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"], 
-        ['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "log/mongrel.pid"]
-      ]
-    end
-
-    def validate
-      @cwd = File.expand_path(@cwd)
-      valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
-
-      Dir.chdir @cwd
-
-      valid_exists? @pid_file, "PID file #@pid_file does not exist.  Not running?"
-      return @valid
-    end
-
-    def run
-      if @force
-        @wait.to_i.times do |waiting|
-          exit(0) if not File.exist? @pid_file
-          sleep 1
-        end
-
-        Mongrel::send_signal("KILL", @pid_file) if File.exist? @pid_file
-      else
-        Mongrel::send_signal("TERM", @pid_file)
-      end
-    end
-  end
-
-
-  class Restart < GemPlugin::Plugin "/commands"
-    include Mongrel::Command::Base
-
-    def configure 
-      options [ 
-        ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, '.'],
-        ['-s', '--soft', "Do a soft restart rather than a process exit restart", :@soft, false],
-        ['-P', '--pid FILE', "Where the PID file is located", :@pid_file, "log/mongrel.pid"]
-      ]
-    end
-
-    def validate
-      @cwd = File.expand_path(@cwd)
-      valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
-
-      Dir.chdir @cwd
-
-      valid_exists? @pid_file, "PID file #@pid_file does not exist.  Not running?"
-      return @valid
-    end
-
-    def run
-      if @soft
-        Mongrel::send_signal("HUP", @pid_file)
-      else
-        Mongrel::send_signal("USR2", @pid_file)
-      end
-    end
-  end
-end
-
-
-GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
-
-
-if not Mongrel::Command::Registry.instance.run ARGV
-  exit 1
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/builder.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/builder.rb
deleted file mode 100644 (file)
index 5f0803a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'mongrel'
-
-class TestPlugin < GemPlugin::Plugin "/handlers"
-  include Mongrel::HttpHandlerPlugin
-
-  def process(request, response)
-    STDERR.puts "My options are: #{options.inspect}"
-    STDERR.puts "Request Was:"
-    STDERR.puts request.params.to_yaml
-  end
-end
-
-config = Mongrel::Configurator.new :host => "127.0.0.1" do
-  load_plugins :includes => ["mongrel"], :excludes => ["rails"]
-  daemonize :cwd => Dir.pwd, :log_file => "mongrel.log", :pid_file => "mongrel.pid"
-  
-  listener :port => 3000 do
-    uri "/app", :handler => plugin("/handlers/testplugin", :test => "that")
-    uri "/app", :handler => Mongrel::DirHandler.new(".")
-    load_plugins :includes => ["mongrel", "rails"]
-  end
-
-  trap("INT") { stop }
-  run
-end
-
-config.join
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/README b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/README
deleted file mode 100644 (file)
index 73ae9a0..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-To get these examples running, install Camping.
-
-Instructions here: http://code.whytheluckystiff.net/camping/
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/blog.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/blog.rb
deleted file mode 100644 (file)
index 81a87da..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-#!/usr/bin/env ruby
-
-$:.unshift File.dirname(__FILE__) + "/../../lib"
-require 'rubygems'
-require_gem 'camping', '>=1.4'
-require 'camping/session'
-  
-Camping.goes :Blog
-
-module Blog
-    include Camping::Session
-end
-
-module Blog::Models
-    def self.schema(&block)
-        @@schema = block if block_given?
-        @@schema
-    end
-  
-    class Post < Base; belongs_to :user; end
-    class Comment < Base; belongs_to :user; end
-    class User < Base; end
-end
-
-Blog::Models.schema do
-    create_table :blog_posts, :force => true do |t|
-      t.column :id,       :integer, :null => false
-      t.column :user_id,  :integer, :null => false
-      t.column :title,    :string,  :limit => 255
-      t.column :body,     :text
-    end
-    create_table :blog_users, :force => true do |t|
-      t.column :id,       :integer, :null => false
-      t.column :username, :string
-      t.column :password, :string
-    end
-    create_table :blog_comments, :force => true do |t|
-      t.column :id,       :integer, :null => false
-      t.column :post_id,  :integer, :null => false
-      t.column :username, :string
-      t.column :body,     :text
-    end
-    execute "INSERT INTO blog_users (username, password) VALUES ('admin', 'camping')"
-end
-
-module Blog::Controllers
-    class Index < R '/'
-        def get
-            @posts = Post.find :all
-            render :index
-        end
-    end
-     
-    class Add
-        def get
-            unless @state.user_id.blank?
-                @user = User.find @state.user_id
-                @post = Post.new
-            end
-            render :add
-        end
-        def post
-            post = Post.create :title => input.post_title, :body => input.post_body,
-                               :user_id => @state.user_id
-            redirect View, post
-        end
-    end
-
-    class Info < R '/info/(\d+)', '/info/(\w+)/(\d+)', '/info', '/info/(\d+)/(\d+)/(\d+)/([\w-]+)'
-        def get(*args)
-            div do
-                code args.inspect; br; br
-                code ENV.inspect; br
-                code "Link: #{R(Info, 1, 2)}"
-            end
-        end
-    end
-
-    class View < R '/view/(\d+)'
-        def get post_id 
-            @post = Post.find post_id
-            @comments = Models::Comment.find :all, :conditions => ['post_id = ?', post_id]
-            render :view
-        end
-    end
-     
-    class Edit < R '/edit/(\d+)', '/edit'
-        def get post_id 
-            unless @state.user_id.blank?
-                @user = User.find @state.user_id
-            end
-            @post = Post.find post_id
-            render :edit
-        end
-     
-        def post
-            @post = Post.find input.post_id
-            @post.update_attributes :title => input.post_title, :body => input.post_body
-            redirect View, @post
-        end
-    end
-     
-    class Comment
-        def post
-            Models::Comment.create(:username => input.post_username,
-                       :body => input.post_body, :post_id => input.post_id)
-            redirect View, input.post_id
-        end
-    end
-     
-    class Login
-        def post
-            @user = User.find :first, :conditions => ['username = ? AND password = ?', input.username, input.password]
-     
-            if @user
-                @login = 'login success !'
-                @state.user_id = @user.id
-            else
-                @login = 'wrong user name or password'
-            end
-            render :login
-        end
-    end
-     
-    class Logout
-        def get
-            @state.user_id = nil
-            render :logout
-        end
-    end
-     
-    class Style < R '/styles.css'
-        def get
-            @headers["Content-Type"] = "text/css; charset=utf-8"
-            @body = %{
-                body {
-                    font-family: Utopia, Georga, serif;
-                }
-                h1.header {
-                    background-color: #fef;
-                    margin: 0; padding: 10px;
-                }
-                div.content {
-                    padding: 10px;
-                }
-            }
-        end
-    end
-end
-
-module Blog::Views
-
-    def layout
-      html do
-        head do
-          title 'blog'
-          link :rel => 'stylesheet', :type => 'text/css', 
-               :href => '/styles.css', :media => 'screen'
-        end
-        body do
-          h1.header { a 'blog', :href => R(Index) }
-          div.content do
-            self << yield
-          end
-        end
-      end
-    end
-
-    def index
-      if @posts.empty?
-        p 'No posts found.'
-        p { a 'Add', :href => R(Add) }
-      else
-        for post in @posts
-          _post(post)
-        end
-      end
-    end
-
-    def login
-      p { b @login }
-      p { a 'Continue', :href => R(Add) }
-    end
-
-    def logout
-      p "You have been logged out."
-      p { a 'Continue', :href => R(Index) }
-    end
-
-    def add
-      if @user
-        _form(post, :action => R(Add))
-      else
-        _login
-      end
-    end
-
-    def edit
-      if @user
-        _form(post, :action => R(Edit))
-      else
-        _login
-      end
-    end
-
-    def view
-        _post(post)
-
-        p "Comment for this post:"
-        for c in @comments
-          h1 c.username
-          p c.body
-        end
-
-        form :action => R(Comment), :method => 'post' do
-          label 'Name', :for => 'post_username'; br
-          input :name => 'post_username', :type => 'text'; br
-          label 'Comment', :for => 'post_body'; br
-          textarea :name => 'post_body' do; end; br
-          input :type => 'hidden', :name => 'post_id', :value => post.id
-          input :type => 'submit'
-        end
-    end
-
-    # partials
-    def _login
-      form :action => R(Login), :method => 'post' do
-        label 'Username', :for => 'username'; br
-        input :name => 'username', :type => 'text'; br
-
-        label 'Password', :for => 'password'; br
-        input :name => 'password', :type => 'text'; br
-
-        input :type => 'submit', :name => 'login', :value => 'Login'
-      end
-    end
-
-    def _post(post)
-      h1 post.title
-      p post.body
-      p do
-        a "Edit", :href => R(Edit, post)
-        a "View", :href => R(View, post)
-      end
-    end
-
-    def _form(post, opts)
-      p do
-        text "You are logged in as #{@user.username} | "
-        a 'Logout', :href => R(Logout)
-      end
-      form({:method => 'post'}.merge(opts)) do
-        label 'Title', :for => 'post_title'; br
-        input :name => 'post_title', :type => 'text', 
-              :value => post.title; br
-
-        label 'Body', :for => 'post_body'; br
-        textarea post.body, :name => 'post_body'; br
-
-        input :type => 'hidden', :name => 'post_id', :value => post.id
-        input :type => 'submit'
-      end
-    end
-end
-def Blog.create
-    Camping::Models::Session.create_schema
-    unless Blog::Models::Post.table_exists?
-        ActiveRecord::Schema.define(&Blog::Models.schema)
-    end
-end
-
-if __FILE__ == $0
-  require 'mongrel/camping'
-
-  Blog::Models::Base.establish_connection :adapter => 'sqlite3', :database => 'blog.db'
-  Blog::Models::Base.logger = Logger.new('camping.log')
-  Blog::Models::Base.threaded_connections=false
-  Blog.create
-  
-  # Use the Configurator as an example rather than Mongrel::Camping.start
-  config = Mongrel::Configurator.new :host => "0.0.0.0" do
-    listener :port => 3002 do
-      uri "/blog", :handler => Mongrel::Camping::CampingHandler.new(Blog)
-      uri "/favicon", :handler => Mongrel::Error404Handler.new("")
-      trap("INT") { stop }
-      run
-    end
-  end
-
-  puts "** Blog example is running at http://localhost:3002/blog"
-  puts "** Default username is `admin', password is `camping'"
-  config.join
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/tepee.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/camping/tepee.rb
deleted file mode 100644 (file)
index 199be37..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/ruby
-$:.unshift File.dirname(__FILE__) + "/../../lib"
-%w(rubygems redcloth camping acts_as_versioned).each { |lib| require lib }
-
-Camping.goes :Tepee
-
-module Tepee::Models
-  def self.schema(&block)
-    @@schema = block if block_given?
-    @@schema
-  end
-  
-  class Page < Base
-    PAGE_LINK = /\[\[([^\]|]*)[|]?([^\]]*)\]\]/
-    validates_uniqueness_of :title
-    before_save { |r| r.title = r.title.underscore }
-    acts_as_versioned
-  end
-end
-
-Tepee::Models.schema do 
-  create_table :tepee_pages, :force => true do |t|
-    t.column :title, :string, :limit => 255
-    t.column :body, :text
-  end
-  Tepee::Models::Page.create_versioned_table
-end
-
-module Tepee::Controllers
-  class Index < R '/'
-    def get
-      redirect Show, 'home_page'
-    end
-  end
-
-  class List < R '/list'
-    def get
-      @pages = Page.find :all, :order => 'title'
-      render :list
-    end
-  end
-
-  class Show < R '/s/(\w+)', '/s/(\w+)/(\d+)'
-    def get page_name, version = nil
-      redirect(Edit, page_name, 1) and return unless @page = Page.find_by_title(page_name)
-      @version = (version.nil? or version == @page.version.to_s) ? @page : @page.versions.find_by_version(version)
-      render :show
-    end
-  end
-
-  class Edit < R '/e/(\w+)/(\d+)', '/e/(\w+)'
-    def get page_name, version = nil
-      @page = Page.find_or_create_by_title(page_name)
-      @page = @page.versions.find_by_version(version) unless version.nil? or version == @page.version.to_s
-      render :edit
-    end
-    
-    def post page_name
-      Page.find_or_create_by_title(page_name).update_attributes :body => input.post_body and redirect Show, page_name
-    end
-  end
-end
-
-module Tepee::Views
-  def layout
-    html do
-      head do
-        title 'test'
-      end
-      body do
-        p do
-          small do
-            span "welcome to " ; a 'tepee', :href => "http://code.whytheluckystiff.net/svn/camping/trunk/examples/tepee/"
-            span '. go ' ;       a 'home',  :href => R(Show, 'home_page')
-            span '. list all ' ; a 'pages', :href => R(List)
-          end
-        end
-        div.content do
-          self << yield
-        end
-      end
-    end
-  end
-
-  def show
-    h1 @page.title
-    div { _markup @version.body }
-    p do 
-      a 'edit',    :href => R(Edit, @version.title, @version.version)
-      a 'back',    :href => R(Show, @version.title, @version.version-1) unless @version.version == 1
-      a 'next',    :href => R(Show, @version.title, @version.version+1) unless @version.version == @page.version
-      a 'current', :href => R(Show, @version.title)                     unless @version.version == @page.version
-    end
-  end
-
-  def edit
-    form :method => 'post', :action => R(Edit, @page.title) do
-      p do
-        label 'Body' ; br
-        textarea @page.body, :name => 'post_body', :rows => 50, :cols => 100
-      end
-      
-      p do
-        input :type => 'submit'
-        a 'cancel', :href => R(Show, @page.title, @page.version)
-      end
-    end
-  end
-
-  def list
-    h1 'all pages'
-    ul { @pages.each { |p| li { a p.title, :href => R(Show, p.title) } } }
-  end
-
-  def _markup body
-    return '' if body.blank?
-    body.gsub!(Tepee::Models::Page::PAGE_LINK) do
-      page = title = $1
-      title = $2 unless $2.empty?
-      page = page.gsub /\W/, '_'
-      if Tepee::Models::Page.find(:all, :select => 'title').collect { |p| p.title }.include?(page)
-        %Q{<a href="#{self/R(Show, page)}">#{title}</a>}
-      else
-        %Q{<span>#{title}<a href="#{self/R(Edit, page, 1)}">?</a></span>}
-      end
-    end
-    RedCloth.new(body, [ :hard_breaks ]).to_html
-  end
-end
-
-def Tepee.create
-  unless Tepee::Models::Page.table_exists?
-    ActiveRecord::Schema.define(&Tepee::Models.schema)
-    Tepee::Models::Page.reset_column_information
-  end
-end
-
-if __FILE__ == $0
-  require 'mongrel/camping'
-
-  Tepee::Models::Base.establish_connection :adapter => 'sqlite3', :database => 'tepee.db'
-  Tepee::Models::Base.logger = Logger.new('camping.log')
-  Tepee::Models::Base.threaded_connections=false
-  Tepee.create
-  
-  server = Mongrel::Camping::start("0.0.0.0",3000,"/tepee",Tepee)
-  puts "** Tepee example is running at http://localhost:3000/tepee"
-  server.acceptor.join
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/httpd.conf b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/httpd.conf
deleted file mode 100644 (file)
index 778124a..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-#
-# This is the main Apache HTTP server configuration file.  It contains the
-# configuration directives that give the server its instructions.
-# See <URL:http://httpd.apache.org/docs/2.2> for detailed information.
-# In particular, see 
-# <URL:http://httpd.apache.org/docs/2.2/mod/directives.html>
-# for a discussion of each configuration directive.
-#
-# Do NOT simply read the instructions in here without understanding
-# what they do.  They're here only as hints or reminders.  If you are unsure
-# consult the online docs. You have been warned.  
-#
-# Configuration and logfile names: If the filenames you specify for many
-# of the server's control files begin with "/" (or "drive:/" for Win32), the
-# server will use that explicit path.  If the filenames do *not* begin
-# with "/", the value of ServerRoot is prepended -- so "logs/foo.log"
-# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
-# server as "/usr/local/apache2/logs/foo.log".
-
-#
-# ServerRoot: The top of the directory tree under which the server's
-# configuration, error, and log files are kept.
-#
-# Do not add a slash at the end of the directory path.  If you point
-# ServerRoot at a non-local disk, be sure to point the LockFile directive
-# at a local disk.  If you wish to share the same ServerRoot for multiple
-# httpd daemons, you will need to change at least LockFile and PidFile.
-#
-ServerRoot "/usr/local/apache2"
-
-#
-# Listen: Allows you to bind Apache to specific IP addresses and/or
-# ports, instead of the default. See also the <VirtualHost>
-# directive.
-#
-# Change this to Listen on specific IP addresses as shown below to 
-# prevent Apache from glomming onto all bound IP addresses.
-#
-#Listen 12.34.56.78:80
-Listen 8088
-
-#
-# Dynamic Shared Object (DSO) Support
-#
-# To be able to use the functionality of a module which was built as a DSO you
-# have to place corresponding `LoadModule' lines at this location so the
-# directives contained in it are actually available _before_ they are used.
-# Statically compiled modules (those listed by `httpd -l') do not need
-# to be loaded here.
-#
-# Example:
-# LoadModule foo_module modules/mod_foo.so
-#
-
-LoadModule deflate_module modules/mod_deflate.so
-LoadModule expires_module modules/mod_expires.so
-LoadModule headers_module modules/mod_headers.so
-LoadModule mime_magic_module modules/mod_mime_magic.so
-LoadModule proxy_module modules/mod_proxy.so
-LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
-LoadModule proxy_http_module modules/mod_proxy_http.so
-LoadModule rewrite_module modules/mod_rewrite.so
-LoadModule version_module modules/mod_version.so
-LoadModule vhost_alias_module modules/mod_vhost_alias.so
-
-<IfModule !mpm_netware_module>
-#
-# If you wish httpd to run as a different user or group, you must run
-# httpd as root initially and it will switch.  
-#
-# User/Group: The name (or #number) of the user/group to run httpd as.
-# It is usually good practice to create a dedicated user and group for
-# running httpd, as with most system services.
-#
-User www-data
-Group www-data
-</IfModule>
-
-# 'Main' server configuration
-#
-# The directives in this section set up the values used by the 'main'
-# server, which responds to any requests that aren't handled by a
-# <VirtualHost> definition.  These values also provide defaults for
-# any <VirtualHost> containers you may define later in the file.
-#
-# All of these directives may appear inside <VirtualHost> containers,
-# in which case these default settings will be overridden for the
-# virtual host being defined.
-#
-
-#
-# ServerAdmin: Your address, where problems with the server should be
-# e-mailed.  This address appears on some server-generated pages, such
-# as error documents.  e.g. admin@your-domain.com
-#
-ServerAdmin admin@SERVER
-
-#
-# ServerName gives the name and port that the server uses to identify itself.
-# This can often be determined automatically, but we recommend you specify
-# it explicitly to prevent problems during startup.
-#
-# If your host doesn't have a registered DNS name, enter its IP address here.
-#
-ServerName SERVER:8088
-
-#
-# DocumentRoot: The directory out of which you will serve your
-# documents. By default, all requests are taken from this directory, but
-# symbolic links and aliases may be used to point to other locations.
-#
-DocumentRoot "/usr/local/apache2/htdocs"
-
-#
-# Each directory to which Apache has access can be configured with respect
-# to which services and features are allowed and/or disabled in that
-# directory (and its subdirectories). 
-#
-# First, we configure the "default" to be a very restrictive set of 
-# features.  
-#
-<Directory />
-    Options FollowSymLinks
-    AllowOverride None
-    Order deny,allow
-    Deny from all
-</Directory>
-
-#
-# Note that from this point forward you must specifically allow
-# particular features to be enabled - so if something's not working as
-# you might expect, make sure that you have specifically enabled it
-# below.
-#
-
-#
-# This should be changed to whatever you set DocumentRoot to.
-#
-<Directory "/usr/local/apache2/htdocs">
-    #
-    # Possible values for the Options directive are "None", "All",
-    # or any combination of:
-    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
-    #
-    # Note that "MultiViews" must be named *explicitly* --- "Options All"
-    # doesn't give it to you.
-    #
-    # The Options directive is both complicated and important.  Please see
-    # http://httpd.apache.org/docs/2.2/mod/core.html#options
-    # for more information.
-    #
-    Options Indexes FollowSymLinks
-
-    #
-    # AllowOverride controls what directives may be placed in .htaccess files.
-    # It can be "All", "None", or any combination of the keywords:
-    #   Options FileInfo AuthConfig Limit
-    #
-    AllowOverride None
-
-    #
-    # Controls who can get stuff from this server.
-    #
-    Order allow,deny
-    Allow from all
-
-</Directory>
-
-#
-# DirectoryIndex: sets the file that Apache will serve if a directory
-# is requested.
-#
-<IfModule dir_module>
-    DirectoryIndex index.html
-</IfModule>
-
-#
-# The following lines prevent .htaccess and .htpasswd files from being 
-# viewed by Web clients. 
-#
-<FilesMatch "^\.ht">
-    Order allow,deny
-    Deny from all
-    Satisfy All
-</FilesMatch>
-
-#
-# ErrorLog: The location of the error log file.
-# If you do not specify an ErrorLog directive within a <VirtualHost>
-# container, error messages relating to that virtual host will be
-# logged here.  If you *do* define an error logfile for a <VirtualHost>
-# container, that host's errors will be logged there and not here.
-#
-ErrorLog logs/error_log
-
-#
-# LogLevel: Control the number of messages logged to the error_log.
-# Possible values include: debug, info, notice, warn, error, crit,
-# alert, emerg.
-#
-LogLevel warn
-
-<IfModule log_config_module>
-    #
-    # The following directives define some format nicknames for use with
-    # a CustomLog directive (see below).
-    #
-    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
-    LogFormat "%h %l %u %t \"%r\" %>s %b" common
-
-    <IfModule logio_module>
-      # You need to enable mod_logio.c to use %I and %O
-      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
-    </IfModule>
-
-    #
-    # The location and format of the access logfile (Common Logfile Format).
-    # If you do not define any access logfiles within a <VirtualHost>
-    # container, they will be logged here.  Contrariwise, if you *do*
-    # define per-<VirtualHost> access logfiles, transactions will be
-    # logged therein and *not* in this file.
-    #
-    CustomLog logs/access_log common
-
-    #
-    # If you prefer a logfile with access, agent, and referer information
-    # (Combined Logfile Format) you can use the following directive.
-    #
-    #CustomLog logs/access_log combined
-</IfModule>
-
-<IfModule alias_module>
-    #
-    # Redirect: Allows you to tell clients about documents that used to 
-    # exist in your server's namespace, but do not anymore. The client 
-    # will make a new request for the document at its new location.
-    # Example:
-    # Redirect permanent /foo http://www.example.com/bar
-
-    #
-    # Alias: Maps web paths into filesystem paths and is used to
-    # access content that does not live under the DocumentRoot.
-    # Example:
-    # Alias /webpath /full/filesystem/path
-    #
-    # If you include a trailing / on /webpath then the server will
-    # require it to be present in the URL.  You will also likely
-    # need to provide a <Directory> section to allow access to
-    # the filesystem path.
-
-    #
-    # ScriptAlias: This controls which directories contain server scripts. 
-    # ScriptAliases are essentially the same as Aliases, except that
-    # documents in the target directory are treated as applications and
-    # run by the server when requested rather than as documents sent to the
-    # client.  The same rules about trailing "/" apply to ScriptAlias
-    # directives as to Alias.
-    #
-    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
-
-</IfModule>
-
-<IfModule cgid_module>
-    #
-    # ScriptSock: On threaded servers, designate the path to the UNIX
-    # socket used to communicate with the CGI daemon of mod_cgid.
-    #
-    #Scriptsock logs/cgisock
-</IfModule>
-
-#
-# "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased
-# CGI directory exists, if you have that configured.
-#
-<Directory "/usr/local/apache2/cgi-bin">
-    AllowOverride None
-    Options None
-    Order allow,deny
-    Allow from all
-</Directory>
-
-#
-# DefaultType: the default MIME type the server will use for a document
-# if it cannot otherwise determine one, such as from filename extensions.
-# If your server contains mostly text or HTML documents, "text/plain" is
-# a good value.  If most of your content is binary, such as applications
-# or images, you may want to use "application/octet-stream" instead to
-# keep browsers from trying to display binary files as though they are
-# text.
-#
-DefaultType text/plain
-
-<IfModule mime_module>
-    #
-    # TypesConfig points to the file containing the list of mappings from
-    # filename extension to MIME-type.
-    #
-    TypesConfig conf/mime.types
-
-    #
-    # AddType allows you to add to or override the MIME configuration
-    # file specified in TypesConfig for specific file types.
-    #
-    #AddType application/x-gzip .tgz
-    #
-    # AddEncoding allows you to have certain browsers uncompress
-    # information on the fly. Note: Not all browsers support this.
-    #
-    #AddEncoding x-compress .Z
-    #AddEncoding x-gzip .gz .tgz
-    #
-    # If the AddEncoding directives above are commented-out, then you
-    # probably should define those extensions to indicate media types:
-    #
-    AddType application/x-compress .Z
-    AddType application/x-gzip .gz .tgz
-
-    #
-    # AddHandler allows you to map certain file extensions to "handlers":
-    # actions unrelated to filetype. These can be either built into the server
-    # or added with the Action directive (see below)
-    #
-    # To use CGI scripts outside of ScriptAliased directories:
-    # (You will also need to add "ExecCGI" to the "Options" directive.)
-    #
-    #AddHandler cgi-script .cgi
-
-    # For type maps (negotiated resources):
-    #AddHandler type-map var
-
-    #
-    # Filters allow you to process content before it is sent to the client.
-    #
-    # To parse .shtml files for server-side includes (SSI):
-    # (You will also need to add "Includes" to the "Options" directive.)
-    #
-    #AddType text/html .shtml
-    #AddOutputFilter INCLUDES .shtml
-</IfModule>
-
-#
-# The mod_mime_magic module allows the server to use various hints from the
-# contents of the file itself to determine its type.  The MIMEMagicFile
-# directive tells the module where the hint definitions are located.
-#
-#MIMEMagicFile conf/magic
-
-#
-# Customizable error responses come in three flavors:
-# 1) plain text 2) local redirects 3) external redirects
-#
-# Some examples:
-#ErrorDocument 500 "The server made a boo boo."
-#ErrorDocument 404 /missing.html
-#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
-#ErrorDocument 402 http://www.example.com/subscription_info.html
-#
-
-#
-# EnableMMAP and EnableSendfile: On systems that support it, 
-# memory-mapping or the sendfile syscall is used to deliver
-# files.  This usually improves server performance, but must
-# be turned off when serving from networked-mounted 
-# filesystems or if support for these functions is otherwise
-# broken on your system.
-#
-#EnableMMAP off
-EnableSendfile on
-
-# Supplemental configuration
-#
-# The configuration files in the conf/extra/ directory can be 
-# included to add extra features or to modify the default configuration of 
-# the server, or you may simply copy their contents here and change as 
-# necessary.
-
-# Server-pool management (MPM specific)
-#Include conf/extra/httpd-mpm.conf
-
-# Multi-language error messages
-#Include conf/extra/httpd-multilang-errordoc.conf
-
-# Fancy directory listings
-#Include conf/extra/httpd-autoindex.conf
-
-# Language settings
-#Include conf/extra/httpd-languages.conf
-
-# User home directories
-#Include conf/extra/httpd-userdir.conf
-
-# Real-time info on requests and configuration
-#Include conf/extra/httpd-info.conf
-
-# Virtual hosts
-#Include conf/extra/httpd-vhosts.conf
-
-# Local access to the Apache HTTP Server Manual
-#Include conf/extra/httpd-manual.conf
-
-# Distributed authoring and versioning (WebDAV)
-#Include conf/extra/httpd-dav.conf
-
-# Various default settings
-#Include conf/extra/httpd-default.conf
-
-# Secure (SSL/TLS) connections
-#Include conf/extra/httpd-ssl.conf
-#
-# Note: The following must must be present to support
-#       starting without SSL on platforms with no /dev/random equivalent
-#       but a statically compiled-in mod_ssl.
-#
-<IfModule ssl_module>
-SSLRandomSeed startup builtin
-SSLRandomSeed connect builtin
-</IfModule>
-
-<VirtualHost *:8088>
-  ServerName SERVER
-  DocumentRoot /var/rails/MYAPP/public
-
-  <Directory "/var/rails/MYAPP/public">
-    Options FollowSymLinks
-    AllowOverride None
-    Order allow,deny
-    Allow from all
-  </Directory>
-
-  # Configure mongrel_cluster 
-  <Proxy balancer://mongrel_cluster>
-    BalancerMember http://127.0.0.1:8000
-    BalancerMember http://127.0.0.1:8001
-  </Proxy>
-
-  RewriteEngine On
-
-  # Uncomment for rewrite debugging 
-  #RewriteLog logs/your_app_rewrite_log
-  #RewriteLogLevel 9 
-
-  # Check for maintenance file and redirect all requests
-  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
-  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
-  RewriteRule ^.*$ /system/maintenance.html [L]
-
-  # Rewrite index to check for static
-  RewriteRule ^/$ /index.html [QSA] 
-
-  # Rewrite to check for Rails cached page
-  RewriteRule ^([^.]+)$ $1.html [QSA]
-
-  # Redirect all non-static requests to cluster
-  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
-  RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]
-
-  # Deflate
-  AddOutputFilterByType DEFLATE text/html text/plain text/xml
-  BrowserMatch ^Mozilla/4 gzip-only-text/html
-  BrowserMatch ^Mozilla/4\.0[678] no-gzip
-  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
-
-  # Uncomment for deflate debugging
-  #DeflateFilterNote Input input_info
-  #DeflateFilterNote Output output_info
-  #DeflateFilterNote Ratio ratio_info
-  #LogFormat '"%r" %{output_info}n/%{input_info}n (%{ratio_info}n%%)' deflate
-  #CustomLog logs/your_app_deflate_log deflate
-
-  ErrorLog logs/your_app_error_log
-  CustomLog logs/your_access_log combined
-</VirtualHost>
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mime.yaml b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mime.yaml
deleted file mode 100644 (file)
index 6e7bb04..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
---- 
-.jpeg: image/jpeg
-.png: image/test
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel.conf b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel.conf
deleted file mode 100644 (file)
index 5c77707..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
---- 
-:environment: production
-:daemon: "true"
-:host: 0.0.0.0
-:log_file: log/mongrel.log
-:docroot: public
-:debug: "false"
-:port: 3000
-:pid_file: log/mongrel.pid
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_ctrl.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_ctrl.rb
deleted file mode 100644 (file)
index 4663d1c..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-###############################################\r
-# mongrel_simple_ctrl.rb\r
-#\r
-# Control script for the Mongrel server\r
-###############################################\r
-require "optparse"\r
-require "win32/service"\r
-include Win32\r
-\r
-# I start the service name with an 'A' so that it appears at the top\r
-SERVICE_NAME = "MongrelSvc"\r
-SERVICE_DISPLAYNAME = "Mongrel HTTP Server"\r
-SCRIPT_ROOT = File.join(File.dirname(__FILE__), '.') \r
-SCRIPT_NAME = "mongrel_simple_service.rb"\r
-SERVICE_SCRIPT = File.expand_path(SCRIPT_ROOT + '/' + SCRIPT_NAME)\r
-\r
-OPTIONS = {}\r
-\r
-ARGV.options do |opts|\r
-   opts.on("-d", "--delete", "Delete the service"){ OPTIONS[:delete] = true }\r
-   opts.on("-u", "--uninstall","Delete the service"){ OPTIONS[:uninstall] = true }\r
-   opts.on("-s", "--start",  "Start the service"){ OPTIONS[:start] = true }\r
-   opts.on("-x", "--stop",   "Stop the service"){ OPTIONS[:stop] = true }\r
-   opts.on("-i", "--install","Install the service"){ OPTIONS[:install] = true }\r
-\r
-   opts.on("-h", "--help",   "Show this help message."){ puts opts; exit }\r
-\r
-   opts.parse!\r
-end\r
-\r
-# Install the service\r
-if OPTIONS[:install]  \r
-   require 'rbconfig'\r
-   \r
-   svc = Service.new\r
-   svc.create_service{ |s|\r
-      s.service_name     = SERVICE_NAME\r
-      s.display_name     = SERVICE_DISPLAYNAME\r
-      s.binary_path_name = Config::CONFIG['bindir'] + '/ruby ' + SERVICE_SCRIPT\r
-      s.dependencies     = []\r
-   }\r
-   svc.close\r
-   puts "#{SERVICE_DISPLAYNAME} service installed"\r
-end\r
-\r
-# Start the service\r
-if OPTIONS[:start]\r
-   Service.start(SERVICE_NAME)\r
-   started = false\r
-   while started == false\r
-      s = Service.status(SERVICE_NAME)\r
-      started = true if s.current_state == "running"\r
-      break if started == true\r
-      puts "One moment, " + s.current_state\r
-      sleep 1\r
-   end\r
-   puts "#{SERVICE_DISPLAYNAME} service started"\r
-end\r
-\r
-# Stop the service\r
-if OPTIONS[:stop]\r
-   Service.stop(SERVICE_NAME)\r
-   stopped = false\r
-   while stopped == false\r
-      s = Service.status(SERVICE_NAME)\r
-      stopped = true if s.current_state == "stopped"\r
-      break if stopped == true\r
-      puts "One moment, " + s.current_state\r
-      sleep 1\r
-   end\r
-   puts "#{SERVICE_DISPLAYNAME} service stopped"\r
-end\r
-\r
-# Delete the service.  Stop it first.\r
-if OPTIONS[:delete] || OPTIONS[:uninstall]\r
-   begin\r
-      Service.stop(SERVICE_NAME)\r
-   rescue\r
-   end\r
-   begin\r
-    Service.delete(SERVICE_NAME)\r
-   rescue\r
-   end\r
-   puts "#{SERVICE_DISPLAYNAME} service deleted"\r
-end\r
-# END mongrel_rails_ctrl.rb\r
-\r
-\r
-\r
-\r
-\r
-\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_service.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/mongrel_simple_service.rb
deleted file mode 100644 (file)
index 3f9bc2c..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-# This script emualtes script/server behavior but running webrick http server \r
-require 'rubygems'\r
-\r
-require 'mongrel'\r
-require 'yaml'\r
-require 'zlib'\r
-\r
-require 'win32/service'\r
-\r
-DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log') \r
-\r
-class SimpleHandler < Mongrel::HttpHandler\r
-    def process(request, response)\r
-      response.start do |head,out|\r
-        head["Content-Type"] = "text/html"\r
-        results = "<html><body>Your request:<br /><pre>#{request.params.to_yaml}</pre><a href=\"/files\">View the files.</a></body></html>"\r
-        if request.params["HTTP_ACCEPT_ENCODING"] == "gzip,deflate"\r
-          head["Content-Encoding"] = "deflate"\r
-          # send it back deflated\r
-          out << Zlib::Deflate.deflate(results)\r
-        else\r
-          # no gzip supported, send it back normal\r
-          out << results\r
-        end\r
-      end\r
-    end\r
-end\r
-\r
-class MongrelDaemon < Win32::Daemon\r
-  def initialize(options)\r
-    @options = options\r
-  end\r
-  \r
-  def service_init\r
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init entered") }\r
-\r
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("Mongrel running on #{@options[:ip]}:#{@options[:port]} with docroot #{@options[:server_root]}") } \r
-\r
-    @simple = SimpleHandler.new\r
-    @files = Mongrel::DirHandler.new(@options[:server_root])\r
-\r
-    @http_server = Mongrel::HttpServer.new(@options[:ip], @options[:port])\r
-    @http_server.register("/", @simple)\r
-    @http_server.register("/files", @files)\r
-\r
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init left") }\r
-  end\r
-  \r
-  def service_stop\r
-    File.open(DEBUG_LOG_FILE,"a+"){ |f|\r
-      f.puts "stop signal received: " + Time.now.to_s\r
-      f.puts "sending stop to mongrel threads: " + Time.now.to_s\r
-    }\r
-    #@http_server.stop\r
-  end\r
-\r
-  def service_pause\r
-    File.open(DEBUG_LOG_FILE,"a+"){ |f|\r
-      f.puts "pause signal received: " + Time.now.to_s\r
-    }\r
-  end\r
-  \r
-  def service_resume\r
-    File.open(DEBUG_LOG_FILE,"a+"){ |f|\r
-      f.puts "continue/resume signal received: " + Time.now.to_s\r
-    }\r
-  end\r
-\r
-  def service_main\r
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main entered") }\r
-    \r
-    begin\r
-      File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - http_server.run") }\r
-      @http_server.run\r
-    \r
-      # No runner thread was needed after all!\r
-      #@runner = Thread.new do\r
-      #  @http_server.acceptor.join\r
-      #end\r
-      #File.open("d:\\test.log","a+") { |f| f.puts("#{Time.now} - runner.run") }\r
-      #@runner.run\r
-      \r
-      # here is where magic happens!\r
-      # if put blocking code here, the thread never left service_main, and the rb_func_call in service.c\r
-      # never exit, even if the stop signal is received.\r
-      #\r
-      # to probe my theory, just comment the while loop and remove the '1' from sleep function\r
-      # service start ok, but fail to stop.\r
-      #\r
-      # Even if no functional code is in service_main (because we have other working threads),\r
-      # we must monitor the state of the service to exit when the STOP event is received.\r
-      #\r
-      # Note: maybe not loop in 1 second intervals?\r
-      while state == RUNNING\r
-        sleep 1\r
-      end\r
-      \r
-    rescue StandardError, Exception, interrupt  => err\r
-      File.open(DEBUG_LOG_FILE,"a+"){ |f| f.puts("#{Time.now} - Error: #{err}") }\r
-      File.open(DEBUG_LOG_FILE,"a+"){ |f| f.puts("BACKTRACE: " + err.backtrace.join("\n")) }\r
-      \r
-    end\r
-    \r
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main left") }\r
-  end\r
-  \r
-end\r
-\r
-OPTIONS = {\r
-  :port            => 3000,\r
-  :ip              => "0.0.0.0",\r
-  :server_root     => File.expand_path(File.dirname(__FILE__)),\r
-}\r
-\r
-web_server = MongrelDaemon.new(OPTIONS)\r
-web_server.mainloop\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/monitrc b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/monitrc
deleted file mode 100644 (file)
index 9964ae9..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-set daemon  60
-set logfile syslog facility log_daemon
-set mailserver localhost
-set mail-format { from: monit@localhost }
-set alert root@localhost
-
-check process sshd with pidfile /var/run/sshd.pid
-   start program  "/etc/init.d/ssh start"
-   stop program  "/etc/init.d/ssh stop"
-   if failed port 22 protocol ssh then restart
-   if 5 restarts within 5 cycles then timeout
-
-check process mysql with pidfile /var/run/mysqld/mysqld.pid
-   group database
-   start program = "/etc/init.d/mysql start"
-   stop program = "/etc/init.d/mysql stop"
-   if failed host 127.0.0.1 port 3306 then restart
-   if 5 restarts within 5 cycles then timeout
-
-check process httpd with pidfile /usr/local/apache2/logs/httpd.pid
-   group www-data
-   start program  "/usr/local/apache2/bin/apachectl start"
-   stop program  "/usr/local/apache2/bin/apachectl stop"
-   if failed host localhost port 80 protocol http
-      and request "/" then alert
-   if cpu is greater than 60% for 2 cycles then alert
-   if cpu > 80% for 5 cycles then restart
-   if children > 250 then restart
-   if loadavg(5min) greater than 10 for 8 cycles then alert
-   if 3 restarts within 5 cycles then timeout
-
-check process mongrel_8000 with pidfile /var/rails/MYAPP/log/mongrel.8000.pid
-   group root
-   if failed host 127.0.0.1 port 8000 protocol http
-      and request "/" then alert
-   if cpu is greater than 60% for 2 cycles then alert
-   if cpu > 80% for 5 cycles then restart
-   if loadavg(5min) greater than 10 for 8 cycles then restart
-   if 3 restarts within 5 cycles then timeout
-
-check process mongrel_8001 with pidfile /var/rails/MYAPP/log/mongrel.8001.pid
-   group root
-   if failed host 127.0.0.1 port 8001 protocol http
-      and request "/" then alert
-   if cpu is greater than 60% for 2 cycles then alert
-   if cpu > 80% for 5 cycles then alert
-   if loadavg(5min) greater than 10 for 8 cycles then alert
-   if 3 restarts within 5 cycles then timeout
-
-check process postfix with pidfile /var/spool/postfix/pid/master.pid
-   group mail
-   start program = "/etc/init.d/postfix start"
-   stop  program = "/etc/init.d/postfix stop"
-   if failed port 25 protocol smtp then restart
-   if 5 restarts within 5 cycles then timeout
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/random_thrash.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/random_thrash.rb
deleted file mode 100644 (file)
index fe9311c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'socket'
-devrand = open("/dev/random","r")
-
-loop do
-  s = TCPSocket.new(ARGV[0],ARGV[1])
-  s.write("GET / HTTP/1.1\r\n")
-  total = 0
-  begin
-    loop do
-       r = devrand.read(10)
-       n = s.write(r)
-       total += n
-    end  
-  rescue Object
-       STDERR.puts "#$!: #{total}"
-  end
-   s.close
-   sleep 1
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/simpletest.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/simpletest.rb
deleted file mode 100644 (file)
index b82e2c6..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-$LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
-require 'mongrel'
-require 'yaml'
-
-class SimpleHandler < Mongrel::HttpHandler
-  def process(request, response)
-    response.start do |head,out|
-      head["Content-Type"] = "text/html"
-      results = "<html><body>Your request:<br /><pre>#{request.params.to_yaml}</pre><a href=\"/files\">View the files.</a></body></html>"
-      out << results
-    end
-  end
-end
-
-class DumbHandler < Mongrel::HttpHandler
-  def process(request, response)
-    response.start do |head,out|
-      head["Content-Type"] = "text/html"
-      out.write("test")
-    end
-  end
-end
-
-
-if ARGV.length != 3
-  STDERR.puts "usage:  simpletest.rb <host> <port> <docroot>"
-  exit(1)
-end
-
-stats = Mongrel::StatisticsFilter.new(:sample_rate => 1)
-
-config = Mongrel::Configurator.new :host => ARGV[0], :port => ARGV[1] do
-  listener do
-    uri "/", :handler => SimpleHandler.new
-    uri "/", :handler => Mongrel::DeflateFilter.new
-    uri "/", :handler => stats
-    uri "/dumb", :handler => DumbHandler.new
-    uri "/dumb", :handler => Mongrel::DeflateFilter.new
-    uri "/dumb", :handler => stats
-    uri "/files", :handler => Mongrel::DirHandler.new(ARGV[2])
-    uri "/files", :handler => stats
-    uri "/status", :handler => Mongrel::StatusHandler.new(:stats_filter => stats)
-    redirect "/redir1", "/"
-    redirect "/to", /to/, 'w'
-  end
-
-  trap("INT") { stop }
-  run
-end
-
-puts "Mongrel running on #{ARGV[0]}:#{ARGV[1]} with docroot #{ARGV[2]}"
-config.join
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/webrick_compare.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/examples/webrick_compare.rb
deleted file mode 100644 (file)
index 15199b0..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/local/bin/ruby
-require 'webrick'
-include WEBrick
-
-s = HTTPServer.new( :Port => 4000 )
-
-# HTTPServer#mount(path, servletclass)
-#   When a request referring "/hello" is received,
-#   the HTTPServer get an instance of servletclass
-#   and then call a method named do_"a HTTP method".
-
-class HelloServlet < HTTPServlet::AbstractServlet
-  def do_GET(req, res)
-    res.body = "hello!"
-    res['Content-Type'] = "text/html"
-  end
-end
-s.mount("/test", HelloServlet)
-
-s.start
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/extconf.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/extconf.rb
deleted file mode 100644 (file)
index e4f6918..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'mkmf'
-
-dir_config("http11")
-have_library("c", "main")
-
-create_makefile("http11")
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.java.rl b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11/http11_parser.java.rl
deleted file mode 100644 (file)
index 71f8d3c..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-package org.jruby.mongrel;
-
-import org.jruby.util.ByteList;
-
-public class Http11Parser {
-
-/** Machine **/
-
-%%{
-  
-  machine http_parser;
-
-  action mark {parser.mark = fpc; }
-
-  action start_field { parser.field_start = fpc; }
-  action write_field { 
-    parser.field_len = fpc-parser.field_start;
-  }
-
-  action start_value { parser.mark = fpc; }
-  action write_value { 
-    if(parser.http_field != null) {
-      parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
-    }
-  }
-  action request_method { 
-    if(parser.request_method != null) 
-      parser.request_method.call(parser.data, parser.mark, fpc-parser.mark);
-  }
-  action request_uri { 
-    if(parser.request_uri != null)
-      parser.request_uri.call(parser.data, parser.mark, fpc-parser.mark);
-  }
-  action fragment { 
-    if(parser.fragment != null)
-      parser.fragment.call(parser.data, parser.mark, fpc-parser.mark);
-  }
-  
-  action start_query {parser.query_start = fpc; }
-  action query_string { 
-    if(parser.query_string != null)
-      parser.query_string.call(parser.data, parser.query_start, fpc-parser.query_start);
-  }
-
-  action http_version {        
-    if(parser.http_version != null)
-      parser.http_version.call(parser.data, parser.mark, fpc-parser.mark);
-  }
-
-  action request_path {
-    if(parser.request_path != null)
-      parser.request_path.call(parser.data, parser.mark, fpc-parser.mark);
-  }
-
-  action done { 
-    parser.body_start = fpc + 1; 
-    if(parser.header_done != null)
-      parser.header_done.call(parser.data, fpc + 1, pe - fpc - 1);
-    fbreak;
-  }
-
-  include http_parser_common "http11_parser_common.rl";
-
-}%%
-
-/** Data **/
-%% write data;
-
-   public static interface ElementCB {
-     public void call(Object data, int at, int length);
-   }
-
-   public static interface FieldCB {
-     public void call(Object data, int field, int flen, int value, int vlen);
-   }
-
-   public static class HttpParser {
-      int cs;
-      int body_start;
-      int content_len;
-      int nread;
-      int mark;
-      int field_start;
-      int field_len;
-      int query_start;
-
-      Object data;
-      ByteList buffer;
-
-      public FieldCB http_field;
-      public ElementCB request_method;
-      public ElementCB request_uri;
-      public ElementCB fragment;
-      public ElementCB request_path;
-      public ElementCB query_string;
-      public ElementCB http_version;
-      public ElementCB header_done;
-
-      public void init() {
-          cs = 0;
-
-          %% write init;
-
-          body_start = 0;
-          content_len = 0;
-          mark = 0;
-          nread = 0;
-          field_len = 0;
-          field_start = 0;
-      }
-   }
-
-   public final HttpParser parser = new HttpParser();
-
-   public int execute(ByteList buffer, int off) {
-     int p, pe;
-     int cs = parser.cs;
-     int len = buffer.realSize;
-     assert off<=len : "offset past end of buffer";
-
-     p = off;
-     pe = len;
-     byte[] data = buffer.bytes;
-     parser.buffer = buffer;
-
-     %% write exec;
-
-     parser.cs = cs;
-     parser.nread += (p - off);
-     
-     assert p <= pe                  : "buffer overflow after parsing execute";
-     assert parser.nread <= len      : "nread longer than length";
-     assert parser.body_start <= len : "body starts after buffer end";
-     assert parser.mark < len        : "mark is after buffer end";
-     assert parser.field_len <= len  : "field has length longer than whole buffer";
-     assert parser.field_start < len : "field starts after buffer end";
-
-     if(parser.body_start>0) {
-        /* final \r\n combo encountered so stop right here */
-        %%write eof;
-        parser.nread++;
-     }
-
-     return parser.nread;
-   }
-
-   public int finish() {
-     int cs = parser.cs;
-
-     %%write eof;
-
-     parser.cs = cs;
-    if(has_error()) {
-      return -1;
-    } else if(is_finished()) {
-      return 1;
-    } else {
-      return 0;
-    }
-  }
-
-  public boolean has_error() {
-    return parser.cs == http_parser_error;
-  }
-
-  public boolean is_finished() {
-    return parser.cs == http_parser_first_final;
-  }
-}
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/Http11Service.java b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/Http11Service.java
deleted file mode 100644 (file)
index 5d78c49..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-import java.io.IOException;
-        
-import org.jruby.Ruby;
-import org.jruby.runtime.load.BasicLibraryService;
-
-import org.jruby.mongrel.Http11;
-
-public class Http11Service implements BasicLibraryService { 
-    public boolean basicLoad(final Ruby runtime) throws IOException {
-        Http11.createHttp11(runtime);
-        return true;
-    }
-}
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11.java b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11.java
deleted file mode 100644 (file)
index 79a950b..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.mongrel;
-
-import org.jruby.Ruby;
-import org.jruby.RubyClass;
-import org.jruby.RubyHash;
-import org.jruby.RubyModule;
-import org.jruby.RubyNumeric;
-import org.jruby.RubyObject;
-import org.jruby.RubyString;
-
-import org.jruby.runtime.CallbackFactory;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.builtin.IRubyObject;
-
-import org.jruby.exceptions.RaiseException;
-
-import org.jruby.util.ByteList;
-
-/**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
-public class Http11 extends RubyObject {
-    public final static int MAX_FIELD_NAME_LENGTH = 256;
-    public final static String MAX_FIELD_NAME_LENGTH_ERR = "HTTP element FIELD_NAME is longer than the 256 allowed length.";
-    public final static int MAX_FIELD_VALUE_LENGTH = 80 * 1024;
-    public final static String MAX_FIELD_VALUE_LENGTH_ERR = "HTTP element FIELD_VALUE is longer than the 81920 allowed length.";
-    public final static int MAX_REQUEST_URI_LENGTH = 1024 * 12;
-    public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
-    public final static int MAX_FRAGMENT_LENGTH = 1024;
-    public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
-    public final static int MAX_REQUEST_PATH_LENGTH = 1024;
-    public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
-    public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
-    public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
-    public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
-    public final static String MAX_HEADER_LENGTH_ERR = "HTTP element HEADER is longer than the 114688 allowed length.";
-
-
-    private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
-        public IRubyObject allocate(Ruby runtime, RubyClass klass) {
-            return new Http11(runtime, klass);
-        }
-    };
-
-    public static void createHttp11(Ruby runtime) {
-        RubyModule mMongrel = runtime.defineModule("Mongrel");
-        mMongrel.defineClassUnder("HttpParserError",runtime.getClass("IOError"),runtime.getClass("IOError").getAllocator());
-
-        CallbackFactory cf = runtime.callbackFactory(Http11.class);
-
-        RubyClass cHttpParser = mMongrel.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
-        cHttpParser.defineFastMethod("initialize",cf.getFastMethod("initialize"));
-        cHttpParser.defineFastMethod("reset",cf.getFastMethod("reset"));
-        cHttpParser.defineFastMethod("finish",cf.getFastMethod("finish"));
-        cHttpParser.defineFastMethod("execute",cf.getFastMethod("execute", IRubyObject.class, IRubyObject.class, IRubyObject.class));
-        cHttpParser.defineFastMethod("error?",cf.getFastMethod("has_error"));
-        cHttpParser.defineFastMethod("finished?",cf.getFastMethod("is_finished"));
-        cHttpParser.defineFastMethod("nread",cf.getFastMethod("nread"));
-    }
-
-    private Ruby runtime;
-    private RubyClass eHttpParserError;
-    private Http11Parser hp;
-
-    public Http11(Ruby runtime, RubyClass clazz) {
-        super(runtime,clazz);
-        this.runtime = runtime;
-        this.eHttpParserError = (RubyClass)runtime.getModule("Mongrel").getConstant("HttpParserError");
-        this.hp = new Http11Parser();
-        this.hp.parser.http_field = http_field;
-        this.hp.parser.request_method = request_method;
-        this.hp.parser.request_uri = request_uri;
-        this.hp.parser.fragment = fragment;
-        this.hp.parser.request_path = request_path;
-        this.hp.parser.query_string = query_string;
-        this.hp.parser.http_version = http_version;
-        this.hp.parser.header_done = header_done;
-        this.hp.parser.init();
-    }
-
-    public void validateMaxLength(int len, int max, String msg) {
-        if(len>max) {
-            throw new RaiseException(runtime, eHttpParserError, msg, true);
-        }
-    }
-
-    private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
-            public void call(Object data, int field, int flen, int value, int vlen) {
-                RubyHash req = (RubyHash)data;
-                RubyString v,f;
-                validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
-                validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
-
-                v = RubyString.newString(runtime, new ByteList(Http11.this.hp.parser.buffer,value,vlen));
-                f = RubyString.newString(runtime, "HTTP_");
-                ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen);
-                for(int i=0,j=b.realSize;i<j;i++) {
-                    if((b.bytes[i]&0xFF) == '-') {
-                        b.bytes[i] = (byte)'_';
-                    } else {
-                        b.bytes[i] = (byte)Character.toUpperCase((char)b.bytes[i]);
-                    }
-                }
-                f.cat(b);
-                req.aset(f,v);
-            }
-        };
-
-    private Http11Parser.ElementCB request_method = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("REQUEST_METHOD"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB request_uri = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                validateMaxLength(length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR);
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("REQUEST_URI"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB fragment = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                validateMaxLength(length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR);
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("FRAGMENT"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB request_path = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                validateMaxLength(length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR);
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("REQUEST_PATH"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB query_string = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                validateMaxLength(length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR);
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("QUERY_STRING"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB http_version = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
-                req.aset(runtime.newString("HTTP_VERSION"),val);
-            }
-        };
-
-    private Http11Parser.ElementCB header_done = new Http11Parser.ElementCB() {
-            public void call(Object data, int at, int length) {
-                RubyHash req = (RubyHash)data;
-                IRubyObject temp,ctype,clen;
-                
-                clen = req.aref(runtime.newString("HTTP_CONTENT_LENGTH"));
-                if(!clen.isNil()) {
-                    req.aset(runtime.newString("CONTENT_LENGTH"),clen);
-                }
-
-                ctype = req.aref(runtime.newString("HTTP_CONTENT_TYPE"));
-                if(!ctype.isNil()) {
-                    req.aset(runtime.newString("CONTENT_TYPE"),ctype);
-                }
-
-                req.aset(runtime.newString("GATEWAY_INTERFACE"),runtime.newString("CGI/1.2"));
-                if(!(temp = req.aref(runtime.newString("HTTP_HOST"))).isNil()) {
-                    String s = temp.toString();
-                    int colon = s.indexOf(':');
-                    if(colon != -1) {
-                        req.aset(runtime.newString("SERVER_NAME"),runtime.newString(s.substring(0,colon)));
-                        req.aset(runtime.newString("SERVER_PORT"),runtime.newString(s.substring(colon+1)));
-                    } else {
-                        req.aset(runtime.newString("SERVER_NAME"),temp);
-                        req.aset(runtime.newString("SERVER_PORT"),runtime.newString("80"));
-                    }
-                }
-
-                req.setInstanceVariable("@http_body", RubyString.newString(runtime, new ByteList(hp.parser.buffer, at, length)));
-                req.aset(runtime.newString("SERVER_PROTOCOL"),runtime.newString("HTTP/1.1"));
-                req.aset(runtime.newString("SERVER_SOFTWARE"),runtime.newString("Mongrel 1.1.5"));
-            }
-        };
-
-    public IRubyObject initialize() {
-        this.hp.parser.init();
-        return this;
-    }
-
-    public IRubyObject reset() {
-        this.hp.parser.init();
-        return runtime.getNil();
-    }
-
-    public IRubyObject finish() {
-        this.hp.finish();
-        return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse();
-    }
-
-    public IRubyObject execute(IRubyObject req_hash, IRubyObject data, IRubyObject start) {
-        int from = 0;
-        from = RubyNumeric.fix2int(start);
-        ByteList d = ((RubyString)data).getByteList();
-        if(from >= d.realSize) {
-            throw new RaiseException(runtime, eHttpParserError, "Requested start is after data buffer end.", true);
-        } else {
-            this.hp.parser.data = req_hash;
-            this.hp.execute(d,from);
-            validateMaxLength(this.hp.parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
-            if(this.hp.has_error()) {
-                throw new RaiseException(runtime, eHttpParserError, "Invalid HTTP format, parsing fails.", true);
-            } else {
-                return runtime.newFixnum(this.hp.parser.nread);
-            }
-        }
-    }
-
-    public IRubyObject has_error() {
-        return this.hp.has_error() ? runtime.getTrue() : runtime.getFalse();
-    }
-
-    public IRubyObject is_finished() {
-        return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse();
-    }
-
-    public IRubyObject nread() {
-        return runtime.newFixnum(this.hp.parser.nread);
-    }
-}// Http11
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11Parser.java b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/ext/http11_java/org/jruby/mongrel/Http11Parser.java
deleted file mode 100644 (file)
index 30f188d..0000000
+++ /dev/null
@@ -1,572 +0,0 @@
-// line 1 "http11_parser.java.rl"
-package org.jruby.mongrel;
-
-import org.jruby.util.ByteList;
-
-public class Http11Parser {
-
-/** Machine **/
-
-// line 64 "http11_parser.java.rl"
-
-
-/** Data **/
-
-// line 16 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-private static void init__http_parser_actions_0( byte[] r )
-{
-       r[0]=0; r[1]=1; r[2]=0; r[3]=1; r[4]=1; r[5]=1; r[6]=2; r[7]=1; 
-       r[8]=3; r[9]=1; r[10]=4; r[11]=1; r[12]=5; r[13]=1; r[14]=6; r[15]=1; 
-       r[16]=7; r[17]=1; r[18]=8; r[19]=1; r[20]=10; r[21]=1; r[22]=11; r[23]=1; 
-       r[24]=12; r[25]=2; r[26]=9; r[27]=6; r[28]=2; r[29]=11; r[30]=6; r[31]=3; 
-       r[32]=8; r[33]=9; r[34]=6; 
-}
-
-private static byte[] create__http_parser_actions( )
-{
-       byte[] r = new byte[35];
-       init__http_parser_actions_0( r );
-       return r;
-}
-
-private static final byte _http_parser_actions[] = create__http_parser_actions();
-
-
-private static void init__http_parser_key_offsets_0( short[] r )
-{
-       r[0]=0; r[1]=0; r[2]=8; r[3]=17; r[4]=27; r[5]=29; r[6]=30; r[7]=31; 
-       r[8]=32; r[9]=33; r[10]=34; r[11]=36; r[12]=39; r[13]=41; r[14]=44; r[15]=45; 
-       r[16]=61; r[17]=62; r[18]=78; r[19]=80; r[20]=81; r[21]=90; r[22]=99; r[23]=105; 
-       r[24]=111; r[25]=121; r[26]=130; r[27]=136; r[28]=142; r[29]=153; r[30]=159; r[31]=165; 
-       r[32]=175; r[33]=181; r[34]=187; r[35]=196; r[36]=205; r[37]=211; r[38]=217; r[39]=226; 
-       r[40]=235; r[41]=244; r[42]=253; r[43]=262; r[44]=271; r[45]=280; r[46]=289; r[47]=298; 
-       r[48]=307; r[49]=316; r[50]=325; r[51]=334; r[52]=343; r[53]=352; r[54]=361; r[55]=370; 
-       r[56]=379; r[57]=380; 
-}
-
-private static short[] create__http_parser_key_offsets( )
-{
-       short[] r = new short[58];
-       init__http_parser_key_offsets_0( r );
-       return r;
-}
-
-private static final short _http_parser_key_offsets[] = create__http_parser_key_offsets();
-
-
-private static void init__http_parser_trans_keys_0( char[] r )
-{
-       r[0]=36; r[1]=95; r[2]=45; r[3]=46; r[4]=48; r[5]=57; r[6]=65; r[7]=90; 
-       r[8]=32; r[9]=36; r[10]=95; r[11]=45; r[12]=46; r[13]=48; r[14]=57; r[15]=65; 
-       r[16]=90; r[17]=42; r[18]=43; r[19]=47; r[20]=58; r[21]=45; r[22]=57; r[23]=65; 
-       r[24]=90; r[25]=97; r[26]=122; r[27]=32; r[28]=35; r[29]=72; r[30]=84; r[31]=84; 
-       r[32]=80; r[33]=47; r[34]=48; r[35]=57; r[36]=46; r[37]=48; r[38]=57; r[39]=48; 
-       r[40]=57; r[41]=13; r[42]=48; r[43]=57; r[44]=10; r[45]=13; r[46]=33; r[47]=124; 
-       r[48]=126; r[49]=35; r[50]=39; r[51]=42; r[52]=43; r[53]=45; r[54]=46; r[55]=48; 
-       r[56]=57; r[57]=65; r[58]=90; r[59]=94; r[60]=122; r[61]=10; r[62]=33; r[63]=58; 
-       r[64]=124; r[65]=126; r[66]=35; r[67]=39; r[68]=42; r[69]=43; r[70]=45; r[71]=46; 
-       r[72]=48; r[73]=57; r[74]=65; r[75]=90; r[76]=94; r[77]=122; r[78]=13; r[79]=32; 
-       r[80]=13; r[81]=32; r[82]=37; r[83]=60; r[84]=62; r[85]=127; r[86]=0; r[87]=31; 
-       r[88]=34; r[89]=35; r[90]=32; r[91]=37; r[92]=60; r[93]=62; r[94]=127; r[95]=0; 
-       r[96]=31; r[97]=34; r[98]=35; r[99]=48; r[100]=57; r[101]=65; r[102]=70; r[103]=97; 
-       r[104]=102; r[105]=48; r[106]=57; r[107]=65; r[108]=70; r[109]=97; r[110]=102; r[111]=43; 
-       r[112]=58; r[113]=45; r[114]=46; r[115]=48; r[116]=57; r[117]=65; r[118]=90; r[119]=97; 
-       r[120]=122; r[121]=32; r[122]=34; r[123]=35; r[124]=37; r[125]=60; r[126]=62; r[127]=127; 
-       r[128]=0; r[129]=31; r[130]=48; r[131]=57; r[132]=65; r[133]=70; r[134]=97; r[135]=102; 
-       r[136]=48; r[137]=57; r[138]=65; r[139]=70; r[140]=97; r[141]=102; r[142]=32; r[143]=34; 
-       r[144]=35; r[145]=37; r[146]=59; r[147]=60; r[148]=62; r[149]=63; r[150]=127; r[151]=0; 
-       r[152]=31; r[153]=48; r[154]=57; r[155]=65; r[156]=70; r[157]=97; r[158]=102; r[159]=48; 
-       r[160]=57; r[161]=65; r[162]=70; r[163]=97; r[164]=102; r[165]=32; r[166]=34; r[167]=35; 
-       r[168]=37; r[169]=60; r[170]=62; r[171]=63; r[172]=127; r[173]=0; r[174]=31; r[175]=48; 
-       r[176]=57; r[177]=65; r[178]=70; r[179]=97; r[180]=102; r[181]=48; r[182]=57; r[183]=65; 
-       r[184]=70; r[185]=97; r[186]=102; r[187]=32; r[188]=34; r[189]=35; r[190]=37; r[191]=60; 
-       r[192]=62; r[193]=127; r[194]=0; r[195]=31; r[196]=32; r[197]=34; r[198]=35; r[199]=37; 
-       r[200]=60; r[201]=62; r[202]=127; r[203]=0; r[204]=31; r[205]=48; r[206]=57; r[207]=65; 
-       r[208]=70; r[209]=97; r[210]=102; r[211]=48; r[212]=57; r[213]=65; r[214]=70; r[215]=97; 
-       r[216]=102; r[217]=32; r[218]=36; r[219]=95; r[220]=45; r[221]=46; r[222]=48; r[223]=57; 
-       r[224]=65; r[225]=90; r[226]=32; r[227]=36; r[228]=95; r[229]=45; r[230]=46; r[231]=48; 
-       r[232]=57; r[233]=65; r[234]=90; r[235]=32; r[236]=36; r[237]=95; r[238]=45; r[239]=46; 
-       r[240]=48; r[241]=57; r[242]=65; r[243]=90; r[244]=32; r[245]=36; r[246]=95; r[247]=45; 
-       r[248]=46; r[249]=48; r[250]=57; r[251]=65; r[252]=90; r[253]=32; r[254]=36; r[255]=95; 
-       r[256]=45; r[257]=46; r[258]=48; r[259]=57; r[260]=65; r[261]=90; r[262]=32; r[263]=36; 
-       r[264]=95; r[265]=45; r[266]=46; r[267]=48; r[268]=57; r[269]=65; r[270]=90; r[271]=32; 
-       r[272]=36; r[273]=95; r[274]=45; r[275]=46; r[276]=48; r[277]=57; r[278]=65; r[279]=90; 
-       r[280]=32; r[281]=36; r[282]=95; r[283]=45; r[284]=46; r[285]=48; r[286]=57; r[287]=65; 
-       r[288]=90; r[289]=32; r[290]=36; r[291]=95; r[292]=45; r[293]=46; r[294]=48; r[295]=57; 
-       r[296]=65; r[297]=90; r[298]=32; r[299]=36; r[300]=95; r[301]=45; r[302]=46; r[303]=48; 
-       r[304]=57; r[305]=65; r[306]=90; r[307]=32; r[308]=36; r[309]=95; r[310]=45; r[311]=46; 
-       r[312]=48; r[313]=57; r[314]=65; r[315]=90; r[316]=32; r[317]=36; r[318]=95; r[319]=45; 
-       r[320]=46; r[321]=48; r[322]=57; r[323]=65; r[324]=90; r[325]=32; r[326]=36; r[327]=95; 
-       r[328]=45; r[329]=46; r[330]=48; r[331]=57; r[332]=65; r[333]=90; r[334]=32; r[335]=36; 
-       r[336]=95; r[337]=45; r[338]=46; r[339]=48; r[340]=57; r[341]=65; r[342]=90; r[343]=32; 
-       r[344]=36; r[345]=95; r[346]=45; r[347]=46; r[348]=48; r[349]=57; r[350]=65; r[351]=90; 
-       r[352]=32; r[353]=36; r[354]=95; r[355]=45; r[356]=46; r[357]=48; r[358]=57; r[359]=65; 
-       r[360]=90; r[361]=32; r[362]=36; r[363]=95; r[364]=45; r[365]=46; r[366]=48; r[367]=57; 
-       r[368]=65; r[369]=90; r[370]=32; r[371]=36; r[372]=95; r[373]=45; r[374]=46; r[375]=48; 
-       r[376]=57; r[377]=65; r[378]=90; r[379]=32; r[380]=0; 
-}
-
-private static char[] create__http_parser_trans_keys( )
-{
-       char[] r = new char[381];
-       init__http_parser_trans_keys_0( r );
-       return r;
-}
-
-private static final char _http_parser_trans_keys[] = create__http_parser_trans_keys();
-
-
-private static void init__http_parser_single_lengths_0( byte[] r )
-{
-       r[0]=0; r[1]=2; r[2]=3; r[3]=4; r[4]=2; r[5]=1; r[6]=1; r[7]=1; 
-       r[8]=1; r[9]=1; r[10]=0; r[11]=1; r[12]=0; r[13]=1; r[14]=1; r[15]=4; 
-       r[16]=1; r[17]=4; r[18]=2; r[19]=1; r[20]=5; r[21]=5; r[22]=0; r[23]=0; 
-       r[24]=2; r[25]=7; r[26]=0; r[27]=0; r[28]=9; r[29]=0; r[30]=0; r[31]=8; 
-       r[32]=0; r[33]=0; r[34]=7; r[35]=7; r[36]=0; r[37]=0; r[38]=3; r[39]=3; 
-       r[40]=3; r[41]=3; r[42]=3; r[43]=3; r[44]=3; r[45]=3; r[46]=3; r[47]=3; 
-       r[48]=3; r[49]=3; r[50]=3; r[51]=3; r[52]=3; r[53]=3; r[54]=3; r[55]=3; 
-       r[56]=1; r[57]=0; 
-}
-
-private static byte[] create__http_parser_single_lengths( )
-{
-       byte[] r = new byte[58];
-       init__http_parser_single_lengths_0( r );
-       return r;
-}
-
-private static final byte _http_parser_single_lengths[] = create__http_parser_single_lengths();
-
-
-private static void init__http_parser_range_lengths_0( byte[] r )
-{
-       r[0]=0; r[1]=3; r[2]=3; r[3]=3; r[4]=0; r[5]=0; r[6]=0; r[7]=0; 
-       r[8]=0; r[9]=0; r[10]=1; r[11]=1; r[12]=1; r[13]=1; r[14]=0; r[15]=6; 
-       r[16]=0; r[17]=6; r[18]=0; r[19]=0; r[20]=2; r[21]=2; r[22]=3; r[23]=3; 
-       r[24]=4; r[25]=1; r[26]=3; r[27]=3; r[28]=1; r[29]=3; r[30]=3; r[31]=1; 
-       r[32]=3; r[33]=3; r[34]=1; r[35]=1; r[36]=3; r[37]=3; r[38]=3; r[39]=3; 
-       r[40]=3; r[41]=3; r[42]=3; r[43]=3; r[44]=3; r[45]=3; r[46]=3; r[47]=3; 
-       r[48]=3; r[49]=3; r[50]=3; r[51]=3; r[52]=3; r[53]=3; r[54]=3; r[55]=3; 
-       r[56]=0; r[57]=0; 
-}
-
-private static byte[] create__http_parser_range_lengths( )
-{
-       byte[] r = new byte[58];
-       init__http_parser_range_lengths_0( r );
-       return r;
-}
-
-private static final byte _http_parser_range_lengths[] = create__http_parser_range_lengths();
-
-
-private static void init__http_parser_index_offsets_0( short[] r )
-{
-       r[0]=0; r[1]=0; r[2]=6; r[3]=13; r[4]=21; r[5]=24; r[6]=26; r[7]=28; 
-       r[8]=30; r[9]=32; r[10]=34; r[11]=36; r[12]=39; r[13]=41; r[14]=44; r[15]=46; 
-       r[16]=57; r[17]=59; r[18]=70; r[19]=73; r[20]=75; r[21]=83; r[22]=91; r[23]=95; 
-       r[24]=99; r[25]=106; r[26]=115; r[27]=119; r[28]=123; r[29]=134; r[30]=138; r[31]=142; 
-       r[32]=152; r[33]=156; r[34]=160; r[35]=169; r[36]=178; r[37]=182; r[38]=186; r[39]=193; 
-       r[40]=200; r[41]=207; r[42]=214; r[43]=221; r[44]=228; r[45]=235; r[46]=242; r[47]=249; 
-       r[48]=256; r[49]=263; r[50]=270; r[51]=277; r[52]=284; r[53]=291; r[54]=298; r[55]=305; 
-       r[56]=312; r[57]=314; 
-}
-
-private static short[] create__http_parser_index_offsets( )
-{
-       short[] r = new short[58];
-       init__http_parser_index_offsets_0( r );
-       return r;
-}
-
-private static final short _http_parser_index_offsets[] = create__http_parser_index_offsets();
-
-
-private static void init__http_parser_indicies_0( byte[] r )
-{
-       r[0]=0; r[1]=0; r[2]=0; r[3]=0; r[4]=0; r[5]=1; r[6]=2; r[7]=3; 
-       r[8]=3; r[9]=3; r[10]=3; r[11]=3; r[12]=1; r[13]=4; r[14]=5; r[15]=6; 
-       r[16]=7; r[17]=5; r[18]=5; r[19]=5; r[20]=1; r[21]=8; r[22]=9; r[23]=1; 
-       r[24]=10; r[25]=1; r[26]=11; r[27]=1; r[28]=12; r[29]=1; r[30]=13; r[31]=1; 
-       r[32]=14; r[33]=1; r[34]=15; r[35]=1; r[36]=16; r[37]=15; r[38]=1; r[39]=17; 
-       r[40]=1; r[41]=18; r[42]=17; r[43]=1; r[44]=19; r[45]=1; r[46]=20; r[47]=21; 
-       r[48]=21; r[49]=21; r[50]=21; r[51]=21; r[52]=21; r[53]=21; r[54]=21; r[55]=21; 
-       r[56]=1; r[57]=22; r[58]=1; r[59]=23; r[60]=24; r[61]=23; r[62]=23; r[63]=23; 
-       r[64]=23; r[65]=23; r[66]=23; r[67]=23; r[68]=23; r[69]=1; r[70]=26; r[71]=27; 
-       r[72]=25; r[73]=26; r[74]=28; r[75]=29; r[76]=31; r[77]=1; r[78]=1; r[79]=1; 
-       r[80]=1; r[81]=1; r[82]=30; r[83]=29; r[84]=33; r[85]=1; r[86]=1; r[87]=1; 
-       r[88]=1; r[89]=1; r[90]=32; r[91]=34; r[92]=34; r[93]=34; r[94]=1; r[95]=32; 
-       r[96]=32; r[97]=32; r[98]=1; r[99]=35; r[100]=36; r[101]=35; r[102]=35; r[103]=35; 
-       r[104]=35; r[105]=1; r[106]=8; r[107]=1; r[108]=9; r[109]=37; r[110]=1; r[111]=1; 
-       r[112]=1; r[113]=1; r[114]=36; r[115]=38; r[116]=38; r[117]=38; r[118]=1; r[119]=36; 
-       r[120]=36; r[121]=36; r[122]=1; r[123]=39; r[124]=1; r[125]=41; r[126]=42; r[127]=43; 
-       r[128]=1; r[129]=1; r[130]=44; r[131]=1; r[132]=1; r[133]=40; r[134]=45; r[135]=45; 
-       r[136]=45; r[137]=1; r[138]=40; r[139]=40; r[140]=40; r[141]=1; r[142]=8; r[143]=1; 
-       r[144]=9; r[145]=47; r[146]=1; r[147]=1; r[148]=48; r[149]=1; r[150]=1; r[151]=46; 
-       r[152]=49; r[153]=49; r[154]=49; r[155]=1; r[156]=46; r[157]=46; r[158]=46; r[159]=1; 
-       r[160]=50; r[161]=1; r[162]=52; r[163]=53; r[164]=1; r[165]=1; r[166]=1; r[167]=1; 
-       r[168]=51; r[169]=54; r[170]=1; r[171]=56; r[172]=57; r[173]=1; r[174]=1; r[175]=1; 
-       r[176]=1; r[177]=55; r[178]=58; r[179]=58; r[180]=58; r[181]=1; r[182]=55; r[183]=55; 
-       r[184]=55; r[185]=1; r[186]=2; r[187]=59; r[188]=59; r[189]=59; r[190]=59; r[191]=59; 
-       r[192]=1; r[193]=2; r[194]=60; r[195]=60; r[196]=60; r[197]=60; r[198]=60; r[199]=1; 
-       r[200]=2; r[201]=61; r[202]=61; r[203]=61; r[204]=61; r[205]=61; r[206]=1; r[207]=2; 
-       r[208]=62; r[209]=62; r[210]=62; r[211]=62; r[212]=62; r[213]=1; r[214]=2; r[215]=63; 
-       r[216]=63; r[217]=63; r[218]=63; r[219]=63; r[220]=1; r[221]=2; r[222]=64; r[223]=64; 
-       r[224]=64; r[225]=64; r[226]=64; r[227]=1; r[228]=2; r[229]=65; r[230]=65; r[231]=65; 
-       r[232]=65; r[233]=65; r[234]=1; r[235]=2; r[236]=66; r[237]=66; r[238]=66; r[239]=66; 
-       r[240]=66; r[241]=1; r[242]=2; r[243]=67; r[244]=67; r[245]=67; r[246]=67; r[247]=67; 
-       r[248]=1; r[249]=2; r[250]=68; r[251]=68; r[252]=68; r[253]=68; r[254]=68; r[255]=1; 
-       r[256]=2; r[257]=69; r[258]=69; r[259]=69; r[260]=69; r[261]=69; r[262]=1; r[263]=2; 
-       r[264]=70; r[265]=70; r[266]=70; r[267]=70; r[268]=70; r[269]=1; r[270]=2; r[271]=71; 
-       r[272]=71; r[273]=71; r[274]=71; r[275]=71; r[276]=1; r[277]=2; r[278]=72; r[279]=72; 
-       r[280]=72; r[281]=72; r[282]=72; r[283]=1; r[284]=2; r[285]=73; r[286]=73; r[287]=73; 
-       r[288]=73; r[289]=73; r[290]=1; r[291]=2; r[292]=74; r[293]=74; r[294]=74; r[295]=74; 
-       r[296]=74; r[297]=1; r[298]=2; r[299]=75; r[300]=75; r[301]=75; r[302]=75; r[303]=75; 
-       r[304]=1; r[305]=2; r[306]=76; r[307]=76; r[308]=76; r[309]=76; r[310]=76; r[311]=1; 
-       r[312]=2; r[313]=1; r[314]=1; r[315]=0; 
-}
-
-private static byte[] create__http_parser_indicies( )
-{
-       byte[] r = new byte[316];
-       init__http_parser_indicies_0( r );
-       return r;
-}
-
-private static final byte _http_parser_indicies[] = create__http_parser_indicies();
-
-
-private static void init__http_parser_trans_targs_wi_0( byte[] r )
-{
-       r[0]=2; r[1]=0; r[2]=3; r[3]=38; r[4]=4; r[5]=24; r[6]=28; r[7]=25; 
-       r[8]=5; r[9]=20; r[10]=6; r[11]=7; r[12]=8; r[13]=9; r[14]=10; r[15]=11; 
-       r[16]=12; r[17]=13; r[18]=14; r[19]=15; r[20]=16; r[21]=17; r[22]=57; r[23]=17; 
-       r[24]=18; r[25]=19; r[26]=14; r[27]=18; r[28]=19; r[29]=5; r[30]=21; r[31]=22; 
-       r[32]=21; r[33]=22; r[34]=23; r[35]=24; r[36]=25; r[37]=26; r[38]=27; r[39]=5; 
-       r[40]=28; r[41]=20; r[42]=29; r[43]=31; r[44]=34; r[45]=30; r[46]=31; r[47]=32; 
-       r[48]=34; r[49]=33; r[50]=5; r[51]=35; r[52]=20; r[53]=36; r[54]=5; r[55]=35; 
-       r[56]=20; r[57]=36; r[58]=37; r[59]=39; r[60]=40; r[61]=41; r[62]=42; r[63]=43; 
-       r[64]=44; r[65]=45; r[66]=46; r[67]=47; r[68]=48; r[69]=49; r[70]=50; r[71]=51; 
-       r[72]=52; r[73]=53; r[74]=54; r[75]=55; r[76]=56; 
-}
-
-private static byte[] create__http_parser_trans_targs_wi( )
-{
-       byte[] r = new byte[77];
-       init__http_parser_trans_targs_wi_0( r );
-       return r;
-}
-
-private static final byte _http_parser_trans_targs_wi[] = create__http_parser_trans_targs_wi();
-
-
-private static void init__http_parser_trans_actions_wi_0( byte[] r )
-{
-       r[0]=1; r[1]=0; r[2]=11; r[3]=0; r[4]=1; r[5]=1; r[6]=1; r[7]=1; 
-       r[8]=13; r[9]=13; r[10]=1; r[11]=0; r[12]=0; r[13]=0; r[14]=0; r[15]=0; 
-       r[16]=0; r[17]=0; r[18]=19; r[19]=0; r[20]=0; r[21]=3; r[22]=23; r[23]=0; 
-       r[24]=5; r[25]=7; r[26]=9; r[27]=7; r[28]=0; r[29]=15; r[30]=1; r[31]=1; 
-       r[32]=0; r[33]=0; r[34]=0; r[35]=0; r[36]=0; r[37]=0; r[38]=0; r[39]=28; 
-       r[40]=0; r[41]=28; r[42]=0; r[43]=21; r[44]=21; r[45]=0; r[46]=0; r[47]=0; 
-       r[48]=0; r[49]=0; r[50]=31; r[51]=17; r[52]=31; r[53]=17; r[54]=25; r[55]=0; 
-       r[56]=25; r[57]=0; r[58]=0; r[59]=0; r[60]=0; r[61]=0; r[62]=0; r[63]=0; 
-       r[64]=0; r[65]=0; r[66]=0; r[67]=0; r[68]=0; r[69]=0; r[70]=0; r[71]=0; 
-       r[72]=0; r[73]=0; r[74]=0; r[75]=0; r[76]=0; 
-}
-
-private static byte[] create__http_parser_trans_actions_wi( )
-{
-       byte[] r = new byte[77];
-       init__http_parser_trans_actions_wi_0( r );
-       return r;
-}
-
-private static final byte _http_parser_trans_actions_wi[] = create__http_parser_trans_actions_wi();
-
-
-static final int http_parser_start = 1;
-static final int http_parser_first_final = 57;
-static final int http_parser_error = 0;
-
-static final int http_parser_en_main = 1;
-
-// line 68 "http11_parser.java.rl"
-
-   public static interface ElementCB {
-     public void call(Object data, int at, int length);
-   }
-
-   public static interface FieldCB {
-     public void call(Object data, int field, int flen, int value, int vlen);
-   }
-
-   public static class HttpParser {
-      int cs;
-      int body_start;
-      int content_len;
-      int nread;
-      int mark;
-      int field_start;
-      int field_len;
-      int query_start;
-
-      Object data;
-      ByteList buffer;
-
-      public FieldCB http_field;
-      public ElementCB request_method;
-      public ElementCB request_uri;
-      public ElementCB fragment;
-      public ElementCB request_path;
-      public ElementCB query_string;
-      public ElementCB http_version;
-      public ElementCB header_done;
-
-      public void init() {
-          cs = 0;
-
-          
-// line 330 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-       {
-       cs = http_parser_start;
-       }
-// line 103 "http11_parser.java.rl"
-
-          body_start = 0;
-          content_len = 0;
-          mark = 0;
-          nread = 0;
-          field_len = 0;
-          field_start = 0;
-      }
-   }
-
-   public final HttpParser parser = new HttpParser();
-
-   public int execute(ByteList buffer, int off) {
-     int p, pe;
-     int cs = parser.cs;
-     int len = buffer.realSize;
-     assert off<=len : "offset past end of buffer";
-
-     p = off;
-     pe = len;
-     byte[] data = buffer.bytes;
-     parser.buffer = buffer;
-
-     
-// line 359 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-       {
-       int _klen;
-       int _trans;
-       int _acts;
-       int _nacts;
-       int _keys;
-
-       if ( p != pe ) {
-       if ( cs != 0 ) {
-       _resume: while ( true ) {
-       _again: do {
-       _match: do {
-       _keys = _http_parser_key_offsets[cs];
-       _trans = _http_parser_index_offsets[cs];
-       _klen = _http_parser_single_lengths[cs];
-       if ( _klen > 0 ) {
-               int _lower = _keys;
-               int _mid;
-               int _upper = _keys + _klen - 1;
-               while (true) {
-                       if ( _upper < _lower )
-                               break;
-
-                       _mid = _lower + ((_upper-_lower) >> 1);
-                       if ( data[p] < _http_parser_trans_keys[_mid] )
-                               _upper = _mid - 1;
-                       else if ( data[p] > _http_parser_trans_keys[_mid] )
-                               _lower = _mid + 1;
-                       else {
-                               _trans += (_mid - _keys);
-                               break _match;
-                       }
-               }
-               _keys += _klen;
-               _trans += _klen;
-       }
-
-       _klen = _http_parser_range_lengths[cs];
-       if ( _klen > 0 ) {
-               int _lower = _keys;
-               int _mid;
-               int _upper = _keys + (_klen<<1) - 2;
-               while (true) {
-                       if ( _upper < _lower )
-                               break;
-
-                       _mid = _lower + (((_upper-_lower) >> 1) & ~1);
-                       if ( data[p] < _http_parser_trans_keys[_mid] )
-                               _upper = _mid - 2;
-                       else if ( data[p] > _http_parser_trans_keys[_mid+1] )
-                               _lower = _mid + 2;
-                       else {
-                               _trans += ((_mid - _keys)>>1);
-                               break _match;
-                       }
-               }
-               _trans += _klen;
-       }
-       } while (false);
-
-       _trans = _http_parser_indicies[_trans];
-       cs = _http_parser_trans_targs_wi[_trans];
-
-       if ( _http_parser_trans_actions_wi[_trans] == 0 )
-               break _again;
-
-       _acts = _http_parser_trans_actions_wi[_trans];
-       _nacts = (int) _http_parser_actions[_acts++];
-       while ( _nacts-- > 0 )
-       {
-               switch ( _http_parser_actions[_acts++] )
-               {
-       case 0:
-// line 13 "http11_parser.java.rl"
-       {parser.mark = p; }
-       break;
-       case 1:
-// line 15 "http11_parser.java.rl"
-       { parser.field_start = p; }
-       break;
-       case 2:
-// line 16 "http11_parser.java.rl"
-       { 
-    parser.field_len = p-parser.field_start;
-  }
-       break;
-       case 3:
-// line 20 "http11_parser.java.rl"
-       { parser.mark = p; }
-       break;
-       case 4:
-// line 21 "http11_parser.java.rl"
-       { 
-    if(parser.http_field != null) {
-      parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, p-parser.mark);
-    }
-  }
-       break;
-       case 5:
-// line 26 "http11_parser.java.rl"
-       { 
-    if(parser.request_method != null) 
-      parser.request_method.call(parser.data, parser.mark, p-parser.mark);
-  }
-       break;
-       case 6:
-// line 30 "http11_parser.java.rl"
-       { 
-    if(parser.request_uri != null)
-      parser.request_uri.call(parser.data, parser.mark, p-parser.mark);
-  }
-       break;
-       case 7:
-// line 34 "http11_parser.java.rl"
-       { 
-    if(parser.fragment != null)
-      parser.fragment.call(parser.data, parser.mark, p-parser.mark);
-  }
-       break;
-       case 8:
-// line 39 "http11_parser.java.rl"
-       {parser.query_start = p; }
-       break;
-       case 9:
-// line 40 "http11_parser.java.rl"
-       { 
-    if(parser.query_string != null)
-      parser.query_string.call(parser.data, parser.query_start, p-parser.query_start);
-  }
-       break;
-       case 10:
-// line 45 "http11_parser.java.rl"
-       {       
-    if(parser.http_version != null)
-      parser.http_version.call(parser.data, parser.mark, p-parser.mark);
-  }
-       break;
-       case 11:
-// line 50 "http11_parser.java.rl"
-       {
-    if(parser.request_path != null)
-      parser.request_path.call(parser.data, parser.mark, p-parser.mark);
-  }
-       break;
-       case 12:
-// line 55 "http11_parser.java.rl"
-       { 
-    parser.body_start = p + 1; 
-    if(parser.header_done != null)
-      parser.header_done.call(parser.data, p + 1, pe - p - 1);
-    if (true) break _resume;
-  }
-       break;
-// line 513 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-               }
-       }
-
-       } while (false);
-       if ( cs == 0 )
-               break _resume;
-       if ( ++p == pe )
-               break _resume;
-       }
-       }       }
-       }
-// line 127 "http11_parser.java.rl"
-
-     parser.cs = cs;
-     parser.nread += (p - off);
-     
-     assert p <= pe                  : "buffer overflow after parsing execute";
-     assert parser.nread <= len      : "nread longer than length";
-     assert parser.body_start <= len : "body starts after buffer end";
-     assert parser.mark < len        : "mark is after buffer end";
-     assert parser.field_len <= len  : "field has length longer than whole buffer";
-     assert parser.field_start < len : "field starts after buffer end";
-
-     if(parser.body_start>0) {
-        /* final \r\n combo encountered so stop right here */
-        
-// line 540 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-// line 141 "http11_parser.java.rl"
-        parser.nread++;
-     }
-
-     return parser.nread;
-   }
-
-   public int finish() {
-     int cs = parser.cs;
-
-     
-// line 552 "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"
-// line 151 "http11_parser.java.rl"
-
-     parser.cs = cs;
-    if(has_error()) {
-      return -1;
-    } else if(is_finished()) {
-      return 1;
-    } else {
-      return 0;
-    }
-  }
-
-  public boolean has_error() {
-    return parser.cs == http_parser_error;
-  }
-
-  public boolean is_finished() {
-    return parser.cs == http_parser_first_final;
-  }
-}
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/http11.so b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/http11.so
deleted file mode 100644 (file)
index 17397b3..0000000
Binary files a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/http11.so and /dev/null differ
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel.rb
deleted file mode 100644 (file)
index d99c56d..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-
-# Standard libraries
-require 'socket'
-require 'tempfile'
-require 'yaml'
-require 'time'
-require 'etc'
-require 'uri'
-require 'stringio'
-
-# Compiled Mongrel extension
-require 'http11'
-
-# Gem conditional loader
-require 'mongrel/gems'
-Mongrel::Gems.require 'cgi_multipart_eof_fix'
-Mongrel::Gems.require 'fastthread'
-require 'thread'
-
-# Ruby Mongrel
-require 'mongrel/cgi'
-require 'mongrel/handlers'
-require 'mongrel/command'
-require 'mongrel/tcphack'
-require 'mongrel/configurator'
-require 'mongrel/uri_classifier'
-require 'mongrel/const'
-require 'mongrel/http_request'
-require 'mongrel/header_out'
-require 'mongrel/http_response'
-
-# Mongrel module containing all of the classes (include C extensions) for running
-# a Mongrel web server.  It contains a minimalist HTTP server with just enough
-# functionality to service web application requests fast as possible.
-module Mongrel
-
-  # Used to stop the HttpServer via Thread.raise.
-  class StopServer < Exception; end
-
-  # Thrown at a thread when it is timed out.
-  class TimeoutError < Exception; end
-
-  # A Hash with one extra parameter for the HTTP body, used internally.
-  class HttpParams < Hash
-    attr_accessor :http_body
-  end
-
-
-  # This is the main driver of Mongrel, while the Mongrel::HttpParser and Mongrel::URIClassifier
-  # make up the majority of how the server functions.  It's a very simple class that just
-  # has a thread accepting connections and a simple HttpServer.process_client function
-  # to do the heavy lifting with the IO and Ruby.  
-  #
-  # You use it by doing the following:
-  #
-  #   server = HttpServer.new("0.0.0.0", 3000)
-  #   server.register("/stuff", MyNiftyHandler.new)
-  #   server.run.join
-  #
-  # The last line can be just server.run if you don't want to join the thread used.
-  # If you don't though Ruby will mysteriously just exit on you.
-  #
-  # Ruby's thread implementation is "interesting" to say the least.  Experiments with
-  # *many* different types of IO processing simply cannot make a dent in it.  Future
-  # releases of Mongrel will find other creative ways to make threads faster, but don't
-  # hold your breath until Ruby 1.9 is actually finally useful.
-  class HttpServer
-    attr_reader :acceptor
-    attr_reader :workers
-    attr_reader :classifier
-    attr_reader :host
-    attr_reader :port
-    attr_reader :throttle
-    attr_reader :timeout
-    attr_reader :num_processors
-
-    # Creates a working server on host:port (strange things happen if port isn't a Number).
-    # Use HttpServer::run to start the server and HttpServer.acceptor.join to 
-    # join the thread that's processing incoming requests on the socket.
-    #
-    # The num_processors optional argument is the maximum number of concurrent
-    # processors to accept, anything over this is closed immediately to maintain
-    # server processing performance.  This may seem mean but it is the most efficient
-    # way to deal with overload.  Other schemes involve still parsing the client's request
-    # which defeats the point of an overload handling system.
-    # 
-    # The throttle parameter is a sleep timeout (in hundredths of a second) that is placed between 
-    # socket.accept calls in order to give the server a cheap throttle time.  It defaults to 0 and
-    # actually if it is 0 then the sleep is not done at all.
-    def initialize(host, port, num_processors=950, throttle=0, timeout=60)
-      
-      tries = 0
-      @socket = TCPServer.new(host, port) 
-      
-      @classifier = URIClassifier.new
-      @host = host
-      @port = port
-      @workers = ThreadGroup.new
-      @throttle = throttle / 100.0
-      @num_processors = num_processors
-      @timeout = timeout
-    end
-
-    # Does the majority of the IO processing.  It has been written in Ruby using
-    # about 7 different IO processing strategies and no matter how it's done 
-    # the performance just does not improve.  It is currently carefully constructed
-    # to make sure that it gets the best possible performance, but anyone who
-    # thinks they can make it faster is more than welcome to take a crack at it.
-    def process_client(client)
-      begin
-        parser = HttpParser.new
-        params = HttpParams.new
-        request = nil
-        data = client.readpartial(Const::CHUNK_SIZE)
-        nparsed = 0
-
-        # Assumption: nparsed will always be less since data will get filled with more
-        # after each parsing.  If it doesn't get more then there was a problem
-        # with the read operation on the client socket.  Effect is to stop processing when the
-        # socket can't fill the buffer for further parsing.
-        while nparsed < data.length
-          nparsed = parser.execute(params, data, nparsed)
-
-          if parser.finished?
-            if not params[Const::REQUEST_PATH]
-              # it might be a dumbass full host request header
-              uri = URI.parse(params[Const::REQUEST_URI])
-              params[Const::REQUEST_PATH] = uri.path
-            end
-
-            raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
-
-            script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
-
-            if handlers
-              params[Const::PATH_INFO] = path_info
-              params[Const::SCRIPT_NAME] = script_name
-
-              # From http://www.ietf.org/rfc/rfc3875 :
-              # "Script authors should be aware that the REMOTE_ADDR and REMOTE_HOST
-              #  meta-variables (see sections 4.1.8 and 4.1.9) may not identify the
-              #  ultimate source of the request.  They identify the client for the
-              #  immediate request to the server; that client may be a proxy, gateway,
-              #  or other intermediary acting on behalf of the actual source client."
-              params[Const::REMOTE_ADDR] = client.peeraddr.last
-
-              # select handlers that want more detailed request notification
-              notifiers = handlers.select { |h| h.request_notify }
-              request = HttpRequest.new(params, client, notifiers)
-
-              # in the case of large file uploads the user could close the socket, so skip those requests
-              break if request.body == nil  # nil signals from HttpRequest::initialize that the request was aborted
-
-              # request is good so far, continue processing the response
-              response = HttpResponse.new(client)
-
-              # Process each handler in registered order until we run out or one finalizes the response.
-              handlers.each do |handler|
-                handler.process(request, response)
-                break if response.done or client.closed?
-              end
-
-              # And finally, if nobody closed the response off, we finalize it.
-              unless response.done or client.closed? 
-                response.finished
-              end
-            else
-              # Didn't find it, return a stock 404 response.
-              client.write(Const::ERROR_404_RESPONSE)
-            end
-
-            break #done
-          else
-            # Parser is not done, queue up more data to read and continue parsing
-            chunk = client.readpartial(Const::CHUNK_SIZE)
-            break if !chunk or chunk.length == 0  # read failed, stop processing
-
-            data << chunk
-            if data.length >= Const::MAX_HEADER
-              raise HttpParserError.new("HEADER is longer than allowed, aborting client early.")
-            end
-          end
-        end
-      rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
-        client.close rescue nil
-      rescue HttpParserError => e
-        STDERR.puts "#{Time.now}: HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}"
-        STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
-      rescue Errno::EMFILE
-        reap_dead_workers('too many files')
-      rescue Object => e
-        STDERR.puts "#{Time.now}: Read error: #{e.inspect}"
-        STDERR.puts e.backtrace.join("\n")
-      ensure
-        begin
-          client.close
-        rescue IOError
-          # Already closed
-        rescue Object => e
-          STDERR.puts "#{Time.now}: Client error: #{e.inspect}"
-          STDERR.puts e.backtrace.join("\n")
-        end
-        request.body.delete if request and request.body.class == Tempfile
-      end
-    end
-
-    # Used internally to kill off any worker threads that have taken too long
-    # to complete processing.  Only called if there are too many processors
-    # currently servicing.  It returns the count of workers still active
-    # after the reap is done.  It only runs if there are workers to reap.
-    def reap_dead_workers(reason='unknown')
-      if @workers.list.length > 0
-        STDERR.puts "#{Time.now}: Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'"
-        error_msg = "Mongrel timed out this thread: #{reason}"
-        mark = Time.now
-        @workers.list.each do |worker|
-          worker[:started_on] = Time.now if not worker[:started_on]
-
-          if mark - worker[:started_on] > @timeout + @throttle
-            STDERR.puts "Thread #{worker.inspect} is too old, killing."
-            worker.raise(TimeoutError.new(error_msg))
-          end
-        end
-      end
-
-      return @workers.list.length
-    end
-
-    # Performs a wait on all the currently running threads and kills any that take
-    # too long.  It waits by @timeout seconds, which can be set in .initialize or
-    # via mongrel_rails. The @throttle setting does extend this waiting period by
-    # that much longer.
-    def graceful_shutdown
-      while reap_dead_workers("shutdown") > 0
-        STDERR.puts "Waiting for #{@workers.list.length} requests to finish, could take #{@timeout + @throttle} seconds."
-        sleep @timeout / 10
-      end
-    end
-
-    def configure_socket_options
-      case RUBY_PLATFORM
-      when /linux/
-        # 9 is currently TCP_DEFER_ACCEPT
-        $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1]
-        $tcp_cork_opts = [Socket::SOL_TCP, 3, 1]
-      when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
-        # Do nothing, just closing a bug when freebsd <= 5.4
-      when /freebsd/
-        # Use the HTTP accept filter if available.
-        # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg
-        unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
-          $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')]
-        end
-      end
-    end
-    
-    # Runs the thing.  It returns the thread used so you can "join" it.  You can also
-    # access the HttpServer::acceptor attribute to get the thread later.
-    def run
-      BasicSocket.do_not_reverse_lookup=true
-
-      configure_socket_options
-
-      if defined?($tcp_defer_accept_opts) and $tcp_defer_accept_opts
-        @socket.setsockopt(*$tcp_defer_accept_opts) rescue nil
-      end
-
-      @acceptor = Thread.new do
-        begin
-          while true
-            begin
-              client = @socket.accept
-  
-              if defined?($tcp_cork_opts) and $tcp_cork_opts
-                client.setsockopt(*$tcp_cork_opts) rescue nil
-              end
-  
-              worker_list = @workers.list
-  
-              if worker_list.length >= @num_processors
-                STDERR.puts "Server overloaded with #{worker_list.length} processors (#@num_processors max). Dropping connection."
-                client.close rescue nil
-                reap_dead_workers("max processors")
-              else
-                thread = Thread.new(client) {|c| process_client(c) }
-                thread[:started_on] = Time.now
-                @workers.add(thread)
-  
-                sleep @throttle if @throttle > 0
-              end
-            rescue StopServer
-              break
-            rescue Errno::EMFILE
-              reap_dead_workers("too many open files")
-              sleep 0.5
-            rescue Errno::ECONNABORTED
-              # client closed the socket even before accept
-              client.close rescue nil
-            rescue Object => e
-              STDERR.puts "#{Time.now}: Unhandled listen loop exception #{e.inspect}."
-              STDERR.puts e.backtrace.join("\n")
-            end
-          end
-          graceful_shutdown
-        ensure
-          @socket.close
-          # STDERR.puts "#{Time.now}: Closed socket."
-        end
-      end
-
-      return @acceptor
-    end
-
-    # Simply registers a handler with the internal URIClassifier.  When the URI is
-    # found in the prefix of a request then your handler's HttpHandler::process method
-    # is called.  See Mongrel::URIClassifier#register for more information.
-    #
-    # If you set in_front=true then the passed in handler will be put in the front of the list
-    # for that particular URI. Otherwise it's placed at the end of the list.
-    def register(uri, handler, in_front=false)
-      begin
-        @classifier.register(uri, [handler])
-      rescue URIClassifier::RegistrationError
-        handlers = @classifier.resolve(uri)[2]
-        method_name = in_front ? 'unshift' : 'push'
-        handlers.send(method_name, handler)
-      end
-      handler.listener = self
-    end
-
-    # Removes any handlers registered at the given URI.  See Mongrel::URIClassifier#unregister
-    # for more information.  Remember this removes them *all* so the entire
-    # processing chain goes away.
-    def unregister(uri)
-      @classifier.unregister(uri)
-    end
-
-    # Stops the acceptor thread and then causes the worker threads to finish
-    # off the request queue before finally exiting.
-    def stop(synchronous=false)
-      @acceptor.raise(StopServer.new)
-
-      if synchronous
-        sleep(0.5) while @acceptor.alive?
-      end
-    end
-
-  end
-end
-
-# Load experimental library, if present. We put it here so it can override anything
-# in regular Mongrel.
-
-$LOAD_PATH.unshift 'projects/mongrel_experimental/lib/'
-Mongrel::Gems.require 'mongrel_experimental', ">=#{Mongrel::Const::MONGREL_VERSION}"
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/camping.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/camping.rb
deleted file mode 100644 (file)
index 31bd196..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'mongrel'
-
-
-module Mongrel
-  # Support for the Camping micro framework at http://camping.rubyforge.org
-  # This implements the unusually long Postamble that Camping usually
-  # needs and shrinks it down to just a single line or two.
-  #
-  # Your Postamble would now be:
-  #
-  #   Mongrel::Camping::start("0.0.0.0",3001,"/tepee",Tepee).join
-  #
-  # If you wish to get fancier than this then you can use the
-  # Camping::CampingHandler directly instead and do your own
-  # wiring:
-  #
-  #   h = Mongrel::HttpServer.new(server, port)
-  #   h.register(uri, CampingHandler.new(Tepee))
-  #   h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
-  #
-  # I add the /favicon.ico since camping apps typically don't 
-  # have them and it's just annoying anyway.
-  module Camping
-
-    # This is a specialized handler for Camping applications
-    # that has them process the request and then translates
-    # the results into something the Mongrel::HttpResponse
-    # needs.
-    class CampingHandler < Mongrel::HttpHandler
-      attr_reader :files
-      attr_reader :guard
-      @@file_only_methods = ["GET","HEAD"]
-
-      def initialize(klass)
-        @files = Mongrel::DirHandler.new(nil, false)
-        @guard = Mutex.new
-        @klass = klass
-      end
-
-      def process(request, response)
-        if response.socket.closed?
-          return
-        end
-
-        controller = nil
-        @guard.synchronize {
-          controller = @klass.run(request.body, request.params)
-        }
-
-        sendfile, clength = nil
-        response.status = controller.status
-        controller.headers.each do |k, v|
-          if k =~ /^X-SENDFILE$/i
-            sendfile = v
-          elsif k =~ /^CONTENT-LENGTH$/i
-            clength = v.to_i
-          else
-            [*v].each do |vi|
-              response.header[k] = vi
-            end
-          end
-        end
-
-        if sendfile
-          request.params[Mongrel::Const::PATH_INFO] = sendfile
-          @files.process(request, response)
-        elsif controller.body.respond_to? :read
-          response.send_status(clength)
-          response.send_header
-          while chunk = controller.body.read(16384)
-            response.write(chunk)
-          end
-          if controller.body.respond_to? :close
-            controller.body.close
-          end
-        else
-          body = controller.body.to_s
-          response.send_status(body.length)
-          response.send_header
-          response.write(body)
-        end
-      end
-    end
-
-    # This is a convenience method that wires up a CampingHandler
-    # for your application on a given port and uri.  It's pretty
-    # much all you need for a camping application to work right.
-    #
-    # It returns the Mongrel::HttpServer which you should either
-    # join or somehow manage.  The thread is running when 
-    # returned.
-
-    def Camping.start(server, port, uri, klass)
-      h = Mongrel::HttpServer.new(server, port)
-      h.register(uri, CampingHandler.new(klass))
-      h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
-      h.run
-      return h
-    end
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/cgi.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/cgi.rb
deleted file mode 100644 (file)
index 4173bde..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'cgi'
-
-module Mongrel
-  # The beginning of a complete wrapper around Mongrel's internal HTTP processing
-  # system but maintaining the original Ruby CGI module.  Use this only as a crutch
-  # to get existing CGI based systems working.  It should handle everything, but please
-  # notify me if you see special warnings.  This work is still very alpha so I need 
-  # testers to help work out the various corner cases.
-  #
-  # The CGIWrapper.handler attribute is normally not set and is available for 
-  # frameworks that need to get back to the handler.  Rails uses this to give
-  # people access to the RailsHandler#files (DirHandler really) so they can
-  # look-up paths and do other things with the files managed there.
-  #
-  # In Rails you can get the real file for a request with:
-  #
-  #  path = @request.cgi.handler.files.can_serve(@request['PATH_INFO'])
-  #
-  # Which is ugly but does the job.  Feel free to write a Rails helper for that.
-  # Refer to DirHandler#can_serve for more information on this.
-  class CGIWrapper < ::CGI
-    public :env_table
-    attr_reader :options
-    attr_accessor :handler
-    # Set this to false if you want calls to CGIWrapper.out to not actually send
-    # the response until you force it.
-    attr_accessor :default_really_final
-
-    # these are stripped out of any keys passed to CGIWrapper.header function
-    REMOVED_KEYS = [ "nph","status","server","connection","type",
-                     "charset","length","language","expires"]
-
-    # Takes an HttpRequest and HttpResponse object, plus any additional arguments
-    # normally passed to CGI.  These are used internally to create a wrapper around
-    # the real CGI while maintaining Mongrel's view of the world.
-    def initialize(request, response, *args)
-      @request = request
-      @response = response
-      @args = *args
-      @input = request.body
-      @head = {}
-      @out_called = false
-      @default_really_final=true
-      super(*args)
-    end
-    
-    # The header is typically called to send back the header.  In our case we
-    # collect it into a hash for later usage.
-    #
-    # nph -- Mostly ignored.  It'll output the date.
-    # connection -- Completely ignored.  Why is CGI doing this?
-    # length -- Ignored since Mongrel figures this out from what you write to output.
-    # 
-    def header(options = "text/html")
-      # if they pass in a string then just write the Content-Type
-      if options.class == String
-        @head['Content-Type'] = options unless @head['Content-Type']
-      else
-        # convert the given options into what Mongrel wants
-        @head['Content-Type'] = options['type'] || "text/html"
-        @head['Content-Type'] += "; charset=" + options['charset'] if options.has_key? "charset" if options['charset']
-        
-        # setup date only if they use nph
-        @head['Date'] = CGI::rfc1123_date(Time.now) if options['nph']
-
-        # setup the server to use the default or what they set
-        @head['Server'] = options['server'] || env_table['SERVER_SOFTWARE']
-
-        # remaining possible options they can give
-        @head['Status'] = options['status'] if options['status']
-        @head['Content-Language'] = options['language'] if options['language']
-        @head['Expires'] = options['expires'] if options['expires']
-
-        # drop the keys we don't want anymore
-        REMOVED_KEYS.each {|k| options.delete(k) }
-
-        # finally just convert the rest raw (which puts 'cookie' directly)
-        # 'cookie' is translated later as we write the header out
-        options.each{|k,v| @head[k] = v}
-      end
-
-      # doing this fakes out the cgi library to think the headers are empty
-      # we then do the real headers in the out function call later
-      ""
-    end
-
-    # Takes any 'cookie' setting and sends it over the Mongrel header,
-    # then removes the setting from the options. If cookie is an 
-    # Array or Hash then it sends those on with .to_s, otherwise
-    # it just calls .to_s on it and hopefully your "cookie" can
-    # write itself correctly.
-    def send_cookies(to)
-      # convert the cookies based on the myriad of possible ways to set a cookie
-      if @head['cookie']
-        cookie = @head['cookie']
-        case cookie
-        when Array
-          cookie.each {|c| to['Set-Cookie'] = c.to_s }
-        when Hash
-          cookie.each_value {|c| to['Set-Cookie'] = c.to_s}
-        else
-          to['Set-Cookie'] = options['cookie'].to_s
-        end
-        
-        @head.delete('cookie')
-      end
-      
-      # @output_cookies seems to never be used, but we'll process it just in case
-      @output_cookies.each {|c| to['Set-Cookie'] = c.to_s } if @output_cookies
-    end
-    
-    # The dumb thing is people can call header or this or both and in any order.
-    # So, we just reuse header and then finalize the HttpResponse the right way.
-    # Status is taken from the various options and converted to what Mongrel needs
-    # via the CGIWrapper.status function.
-    #
-    # We also prevent Rails from actually doing the final send by adding a
-    # second parameter "really_final".  Only Mongrel calls this after Rails
-    # is done.  Since this will break other frameworks, it defaults to 
-    # a different setting for rails (false) and (true) for others.
-    def out(options = "text/html", really_final=@default_really_final)
-      if @out_called || !really_final
-        # don't do it more than once or if it's not the really final call
-        return
-      end
-
-      header(options)
-
-      @response.start status do |head, body|
-        send_cookies(head)
-        
-        @head.each {|k,v| head[k] = v}
-        body.write(yield || "")
-      end
-
-      @out_called = true
-    end
-    
-    # Computes the status once, but lazily so that people who call header twice
-    # don't get penalized.  Because CGI insists on including the options status 
-    # message in the status we have to do a bit of parsing.
-    def status
-      if not @status
-        stat = @head["Status"]
-        stat = stat.split(' ')[0] if stat
-
-        @status = stat || "200"
-      end
-
-      @status
-    end
-
-    # Used to wrap the normal args variable used inside CGI.
-    def args
-      @args
-    end
-    
-    # Used to wrap the normal env_table variable used inside CGI.
-    def env_table
-      @request.params
-    end
-    
-    # Used to wrap the normal stdinput variable used inside CGI.
-    def stdinput
-      @input
-    end
-    
-    # The stdoutput should be completely bypassed but we'll drop a warning just in case
-    def stdoutput
-      STDERR.puts "WARNING: Your program is doing something not expected.  Please tell Zed that stdoutput was used and what software you are running.  Thanks."
-      @response.body
-    end    
-
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/command.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/command.rb
deleted file mode 100644 (file)
index a3ee57d..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'singleton'
-require 'optparse'
-
-require 'mongrel/gems'
-Mongrel::Gems.require 'gem_plugin'
-
-module Mongrel
-
-  # Contains all of the various commands that are used with 
-  # Mongrel servers.
-
-  module Command
-
-    BANNER = "Usage: mongrel_rails <command> [options]"
-
-    # A Command pattern implementation used to create the set of command available to the user
-    # from Mongrel.  The script uses objects which implement this interface to do the
-    # user's bidding.
-    module Base
-
-      attr_reader :valid, :done_validating, :original_args
-
-      # Called by the implemented command to set the options for that command.
-      # Every option has a short and long version, a description, a variable to
-      # set, and a default value.  No exceptions.
-      def options(opts)
-        # process the given options array
-        opts.each do |short, long, help, variable, default|
-          self.instance_variable_set(variable, default)
-          @opt.on(short, long, help) do |arg|
-            self.instance_variable_set(variable, arg)
-          end
-        end
-      end
-
-      # Called by the subclass to setup the command and parse the argv arguments.
-      # The call is destructive on argv since it uses the OptionParser#parse! function.
-      def initialize(options={})
-        argv = options[:argv] || []
-        @opt = OptionParser.new
-        @opt.banner = Mongrel::Command::BANNER
-        @valid = true
-        # this is retarded, but it has to be done this way because -h and -v exit
-        @done_validating = false
-        @original_args = argv.dup
-
-        configure
-
-        # I need to add my own -h definition to prevent the -h by default from exiting.
-        @opt.on_tail("-h", "--help", "Show this message") do
-          @done_validating = true
-          puts @opt
-        end
-
-        # I need to add my own -v definition to prevent the -v from exiting by default as well.
-        @opt.on_tail("--version", "Show version") do
-          @done_validating = true
-          if VERSION
-            puts "Version #{Mongrel::Const::MONGREL_VERSION}"
-          end
-        end
-
-        @opt.parse! argv
-      end
-
-      def configure
-        options []
-      end
-
-      # Returns true/false depending on whether the command is configured properly.
-      def validate
-        return @valid
-      end
-
-      # Returns a help message.  Defaults to OptionParser#help which should be good.
-      def help
-        @opt.help
-      end
-
-      # Runs the command doing it's job.  You should implement this otherwise it will
-      # throw a NotImplementedError as a reminder.
-      def run
-        raise NotImplementedError
-      end
-
-
-      # Validates the given expression is true and prints the message if not, exiting.
-      def valid?(exp, message)
-        if not @done_validating and (not exp)
-          failure message
-          @valid = false
-          @done_validating = true
-        end
-      end
-
-      # Validates that a file exists and if not displays the message
-      def valid_exists?(file, message)
-        valid?(file != nil && File.exist?(file), message)
-      end
-
-
-      # Validates that the file is a file and not a directory or something else.
-      def valid_file?(file, message)
-        valid?(file != nil && File.file?(file), message)
-      end
-
-      # Validates that the given directory exists
-      def valid_dir?(file, message)
-        valid?(file != nil && File.directory?(file), message)
-      end
-
-      def valid_user?(user)
-        valid?(@group, "You must also specify a group.")
-        begin
-          Etc.getpwnam(user)
-        rescue
-          failure "User does not exist: #{user}"
-          @valid = false
-        end
-      end
-
-      def valid_group?(group)
-        valid?(@user, "You must also specify a user.")
-        begin
-          Etc.getgrnam(group)
-        rescue
-          failure "Group does not exist: #{group}"
-          @valid = false
-        end
-      end
-
-      # Just a simple method to display failure until something better is developed.
-      def failure(message)
-        STDERR.puts "!!! #{message}"
-      end
-    end
-
-    # A Singleton class that manages all of the available commands
-    # and handles running them.
-    class Registry
-      include Singleton
-
-      # Builds a list of possible commands from the Command derivates list
-      def commands
-        pmgr = GemPlugin::Manager.instance
-        list = pmgr.plugins["/commands"].keys
-        return list.sort
-      end
-
-      # Prints a list of available commands.
-      def print_command_list
-        puts "#{Mongrel::Command::BANNER}\nAvailable commands are:\n\n"
-
-        self.commands.each do |name|
-          if /mongrel::/ =~ name
-            name = name[9 .. -1]
-          end
-
-          puts " - #{name[1 .. -1]}\n"
-        end
-
-        puts "\nEach command takes -h as an option to get help."
-
-      end
-
-
-      # Runs the args against the first argument as the command name.
-      # If it has any errors it returns a false, otherwise it return true.
-      def run(args)
-        # find the command
-        cmd_name = args.shift
-
-        if !cmd_name or cmd_name == "?" or cmd_name == "help"
-          print_command_list
-          return true
-        elsif cmd_name == "--version"
-          puts "Mongrel Web Server #{Mongrel::Const::MONGREL_VERSION}"
-          return true
-        end
-
-        begin
-          # quick hack so that existing commands will keep working but the Mongrel:: ones can be moved
-          if ["start", "stop", "restart"].include? cmd_name
-            cmd_name = "mongrel::" + cmd_name
-          end
-
-          command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
-        rescue OptionParser::InvalidOption
-          STDERR.puts "#$! for command '#{cmd_name}'"
-          STDERR.puts "Try #{cmd_name} -h to get help."
-          return false
-        rescue
-          STDERR.puts "ERROR RUNNING '#{cmd_name}': #$!"
-          STDERR.puts "Use help command to get help"
-          return false
-        end
-
-        # Normally the command is NOT valid right after being created
-        # but sometimes (like with -h or -v) there's no further processing
-        # needed so the command is already valid so we can skip it.
-        if not command.done_validating
-          if not command.validate
-            STDERR.puts "#{cmd_name} reported an error. Use mongrel_rails #{cmd_name} -h to get help."
-            return false
-          else
-            command.run
-          end
-        end
-
-        return true
-      end
-
-    end
-  end
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/configurator.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/configurator.rb
deleted file mode 100644 (file)
index bbb88ba..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-require 'yaml'
-require 'etc'
-
-module Mongrel
-  # Implements a simple DSL for configuring a Mongrel server for your 
-  # purposes.  More used by framework implementers to setup Mongrel
-  # how they like, but could be used by regular folks to add more things
-  # to an existing mongrel configuration.
-  #
-  # It is used like this:
-  #
-  #   require 'mongrel'
-  #   config = Mongrel::Configurator.new :host => "127.0.0.1" do
-  #     listener :port => 3000 do
-  #       uri "/app", :handler => Mongrel::DirHandler.new(".", load_mime_map("mime.yaml"))
-  #     end
-  #     run
-  #   end
-  # 
-  # This will setup a simple DirHandler at the current directory and load additional
-  # mime types from mimy.yaml.  The :host => "127.0.0.1" is actually not 
-  # specific to the servers but just a hash of default parameters that all 
-  # server or uri calls receive.
-  #
-  # When you are inside the block after Mongrel::Configurator.new you can simply
-  # call functions that are part of Configurator (like server, uri, daemonize, etc)
-  # without having to refer to anything else.  You can also call these functions on 
-  # the resulting object directly for additional configuration.
-  #
-  # A major thing about Configurator is that it actually lets you configure 
-  # multiple listeners for any hosts and ports you want.  These are kept in a
-  # map config.listeners so you can get to them.
-  #
-  # * :pid_file => Where to write the process ID.
-  class Configurator
-    attr_reader :listeners
-    attr_reader :defaults
-    attr_reader :needs_restart
-
-    # You pass in initial defaults and then a block to continue configuring.
-    def initialize(defaults={}, &block)
-      @listener = nil
-      @listener_name = nil
-      @listeners = {}
-      @defaults = defaults
-      @needs_restart = false
-      @pid_file = defaults[:pid_file]
-
-      if block
-        cloaker(&block).bind(self).call
-      end
-    end
-
-    # Change privileges of the process to specified user and group.
-    def change_privilege(user, group)
-      begin
-        uid, gid = Process.euid, Process.egid
-        target_uid = Etc.getpwnam(user).uid if user
-        target_gid = Etc.getgrnam(group).gid if group
-
-        if uid != target_uid or gid != target_gid
-          log "Initiating groups for #{user.inspect}:#{group.inspect}."
-          Process.initgroups(user, target_gid)
-        
-          log "Changing group to #{group.inspect}."
-          Process::GID.change_privilege(target_gid)
-
-          log "Changing user to #{user.inspect}." 
-          Process::UID.change_privilege(target_uid)
-        end
-      rescue Errno::EPERM => e
-        log "Couldn't change user and group to #{user.inspect}:#{group.inspect}: #{e.to_s}."
-        log "Mongrel failed to start."
-        exit 1
-      end
-    end
-
-    def remove_pid_file
-      File.unlink(@pid_file) if @pid_file and File.exists?(@pid_file)
-    end
-
-    # Writes the PID file if we're not on Windows.
-    def write_pid_file
-      if RUBY_PLATFORM !~ /mswin/
-        log "Writing PID file to #{@pid_file}"
-        open(@pid_file,"w") {|f| f.write(Process.pid) }
-        open(@pid_file,"w") do |f|
-          f.write(Process.pid)
-          File.chmod(0644, @pid_file)
-        end      
-      end
-    end
-
-    # Generates a class for cloaking the current self and making the DSL nicer.
-    def cloaking_class
-      class << self
-        self
-      end
-    end
-
-    # Do not call this.  You were warned.
-    def cloaker(&block)
-      cloaking_class.class_eval do
-        define_method :cloaker_, &block
-        meth = instance_method( :cloaker_ )
-        remove_method :cloaker_
-        meth
-      end
-    end
-
-    # This will resolve the given options against the defaults.
-    # Normally just used internally.
-    def resolve_defaults(options)
-      options.merge(@defaults)
-    end
-
-    # Starts a listener block.  This is the only one that actually takes
-    # a block and then you make Configurator.uri calls in order to setup
-    # your URIs and handlers.  If you write your Handlers as GemPlugins
-    # then you can use load_plugins and plugin to load them.
-    # 
-    # It expects the following options (or defaults):
-    # 
-    # * :host => Host name to bind.
-    # * :port => Port to bind.
-    # * :num_processors => The maximum number of concurrent threads allowed.
-    # * :throttle => Time to pause (in hundredths of a second) between accepting clients. 
-    # * :timeout => Time to wait (in seconds) before killing a stalled thread.
-    # * :user => User to change to, must have :group as well.
-    # * :group => Group to change to, must have :user as well.
-    #
-    def listener(options={},&block)
-      raise "Cannot call listener inside another listener block." if (@listener or @listener_name)
-      ops = resolve_defaults(options)
-      ops[:num_processors] ||= 950
-      ops[:throttle] ||= 0
-      ops[:timeout] ||= 60
-
-      @listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:throttle].to_i, ops[:timeout].to_i)
-      @listener_name = "#{ops[:host]}:#{ops[:port]}"
-      @listeners[@listener_name] = @listener
-
-      if ops[:user] and ops[:group]
-        change_privilege(ops[:user], ops[:group])
-      end
-
-      # Does the actual cloaking operation to give the new implicit self.
-      if block
-        cloaker(&block).bind(self).call
-      end
-
-      # all done processing this listener setup, reset implicit variables
-      @listener = nil
-      @listener_name = nil
-    end
-
-
-    # Called inside a Configurator.listener block in order to 
-    # add URI->handler mappings for that listener.  Use this as
-    # many times as you like.  It expects the following options
-    # or defaults:
-    #
-    # * :handler => HttpHandler -- Handler to use for this location.
-    # * :in_front => true/false -- Rather than appending, it prepends this handler.
-    def uri(location, options={})
-      ops = resolve_defaults(options)
-      @listener.register(location, ops[:handler], ops[:in_front])
-    end
-
-
-    # Daemonizes the current Ruby script turning all the
-    # listeners into an actual "server" or detached process.
-    # You must call this *before* frameworks that open files
-    # as otherwise the files will be closed by this function.
-    #
-    # Does not work for Win32 systems (the call is silently ignored).
-    #
-    # Requires the following options or defaults:
-    #
-    # * :cwd => Directory to change to.
-    # * :log_file => Where to write STDOUT and STDERR.
-    # 
-    # It is safe to call this on win32 as it will only require the daemons
-    # gem/library if NOT win32.
-    def daemonize(options={})
-      ops = resolve_defaults(options)
-      # save this for later since daemonize will hose it
-      if RUBY_PLATFORM !~ /mswin/
-        require 'daemons/daemonize'
-
-        logfile = ops[:log_file]
-        if logfile[0].chr != "/"
-          logfile = File.join(ops[:cwd],logfile)
-          if not File.exist?(File.dirname(logfile))
-            log "!!! Log file directory not found at full path #{File.dirname(logfile)}.  Update your configuration to use a full path."
-            exit 1
-          end
-        end
-
-        Daemonize.daemonize(logfile)
-
-        # change back to the original starting directory
-        Dir.chdir(ops[:cwd])
-
-      else
-        log "WARNING: Win32 does not support daemon mode."
-      end
-    end
-
-
-    # Uses the GemPlugin system to easily load plugins based on their
-    # gem dependencies.  You pass in either an :includes => [] or 
-    # :excludes => [] setting listing the names of plugins to include
-    # or exclude from the determining the dependencies.
-    def load_plugins(options={})
-      ops = resolve_defaults(options)
-
-      load_settings = {}
-      if ops[:includes]
-        ops[:includes].each do |plugin|
-          load_settings[plugin] = GemPlugin::INCLUDE
-        end
-      end
-
-      if ops[:excludes]
-        ops[:excludes].each do |plugin|
-          load_settings[plugin] = GemPlugin::EXCLUDE
-        end
-      end
-
-      GemPlugin::Manager.instance.load(load_settings)
-    end
-
-
-    # Easy way to load a YAML file and apply default settings.
-    def load_yaml(file, default={})
-      default.merge(YAML.load_file(file))
-    end
-
-
-    # Loads the MIME map file and checks that it is correct
-    # on loading.  This is commonly passed to Mongrel::DirHandler
-    # or any framework handler that uses DirHandler to serve files.
-    # You can also include a set of default MIME types as additional
-    # settings.  See Mongrel::DirHandler for how the MIME types map
-    # is organized.
-    def load_mime_map(file, mime={})
-      # configure any requested mime map
-      mime = load_yaml(file, mime)
-
-      # check all the mime types to make sure they are the right format
-      mime.each {|k,v| log "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
-
-      return mime
-    end
-
-
-    # Loads and creates a plugin for you based on the given
-    # name and configured with the selected options.  The options
-    # are merged with the defaults prior to passing them in.
-    def plugin(name, options={})
-      ops = resolve_defaults(options)
-      GemPlugin::Manager.instance.create(name, ops)
-    end
-
-    # Lets you do redirects easily as described in Mongrel::RedirectHandler.
-    # You use it inside the configurator like this:
-    #
-    #   redirect("/test", "/to/there") # simple
-    #   redirect("/to", /t/, 'w') # regexp
-    #   redirect("/hey", /(w+)/) {|match| ...}  # block
-    #
-    def redirect(from, pattern, replacement = nil, &block)
-      uri from, :handler => Mongrel::RedirectHandler.new(pattern, replacement, &block)
-    end
-
-    # Works like a meta run method which goes through all the 
-    # configured listeners.  Use the Configurator.join method
-    # to prevent Ruby from exiting until each one is done.
-    def run
-      @listeners.each {|name,s| 
-        s.run 
-      }
-
-      $mongrel_sleeper_thread = Thread.new { loop { sleep 1 } }
-    end
-
-    # Calls .stop on all the configured listeners so they
-    # stop processing requests (gracefully).  By default it
-    # assumes that you don't want to restart.
-    def stop(needs_restart=false, synchronous=false)   
-      @listeners.each do |name,s| 
-        s.stop(synchronous)      
-      end      
-      @needs_restart = needs_restart
-    end
-
-
-    # This method should actually be called *outside* of the
-    # Configurator block so that you can control it.  In other words
-    # do it like:  config.join.
-    def join
-      @listeners.values.each {|s| s.acceptor.join }
-    end
-
-
-    # Calling this before you register your URIs to the given location
-    # will setup a set of handlers that log open files, objects, and the
-    # parameters for each request.  This helps you track common problems
-    # found in Rails applications that are either slow or become unresponsive
-    # after a little while.
-    #
-    # You can pass an extra parameter *what* to indicate what you want to 
-    # debug.  For example, if you just want to dump rails stuff then do:
-    #
-    #   debug "/", what = [:rails]
-    # 
-    # And it will only produce the log/mongrel_debug/rails.log file.
-    # Available options are: :access, :files, :objects, :threads, :rails 
-    # 
-    # NOTE: Use [:files] to get accesses dumped to stderr like with WEBrick.
-    def debug(location, what = [:access, :files, :objects, :threads, :rails])
-      require 'mongrel/debug'
-      handlers = {
-        :access => "/handlers/requestlog::access", 
-        :files => "/handlers/requestlog::files", 
-        :objects => "/handlers/requestlog::objects", 
-        :threads => "/handlers/requestlog::threads",
-        :rails => "/handlers/requestlog::params"
-      }
-
-      # turn on the debugging infrastructure, and ObjectTracker is a pig
-      MongrelDbg.configure
-
-      # now we roll through each requested debug type, turn it on and load that plugin
-      what.each do |type| 
-        MongrelDbg.begin_trace type 
-        uri location, :handler => plugin(handlers[type])
-      end
-    end
-
-    # Used to allow you to let users specify their own configurations
-    # inside your Configurator setup.  You pass it a script name and
-    # it reads it in and does an eval on the contents passing in the right
-    # binding so they can put their own Configurator statements.
-    def run_config(script)
-      open(script) {|f| eval(f.read, proc {self}) }
-    end
-
-    # Sets up the standard signal handlers that are used on most Ruby
-    # It only configures if the platform is not win32 and doesn't do
-    # a HUP signal since this is typically framework specific.
-    #
-    # Requires a :pid_file option given to Configurator.new to indicate a file to delete.  
-    # It sets the MongrelConfig.needs_restart attribute if 
-    # the start command should reload.  It's up to you to detect this
-    # and do whatever is needed for a "restart".
-    #
-    # This command is safely ignored if the platform is win32 (with a warning)
-    def setup_signals(options={})
-      ops = resolve_defaults(options)
-
-      # forced shutdown, even if previously restarted (actually just like TERM but for CTRL-C)
-      trap("INT") { log "INT signal received."; stop(false) }
-
-      # clean up the pid file always
-      at_exit { remove_pid_file }
-
-      if RUBY_PLATFORM !~ /mswin/
-        # graceful shutdown
-        trap("TERM") { log "TERM signal received."; stop }
-        trap("USR1") { log "USR1 received, toggling $mongrel_debug_client to #{!$mongrel_debug_client}"; $mongrel_debug_client = !$mongrel_debug_client }
-        # restart
-        trap("USR2") { log "USR2 signal received."; stop(true) }
-
-        log "Signals ready.  TERM => stop.  USR2 => restart.  INT => stop (no restart)."
-      else
-        log "Signals ready.  INT => stop (no restart)."
-      end
-    end
-
-    # Logs a simple message to STDERR (or the mongrel log if in daemon mode).
-    def log(msg)
-      STDERR.print "** ", msg, "\n"
-    end
-
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/const.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/const.rb
deleted file mode 100644 (file)
index 9b4109e..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-
-module Mongrel
-
-  # Every standard HTTP code mapped to the appropriate message.  These are
-  # used so frequently that they are placed directly in Mongrel for easy
-  # access rather than Mongrel::Const itself.
-  HTTP_STATUS_CODES = {  
-    100  => 'Continue', 
-    101  => 'Switching Protocols', 
-    200  => 'OK', 
-    201  => 'Created', 
-    202  => 'Accepted', 
-    203  => 'Non-Authoritative Information', 
-    204  => 'No Content', 
-    205  => 'Reset Content', 
-    206  => 'Partial Content', 
-    300  => 'Multiple Choices', 
-    301  => 'Moved Permanently', 
-    302  => 'Moved Temporarily', 
-    303  => 'See Other', 
-    304  => 'Not Modified', 
-    305  => 'Use Proxy', 
-    400  => 'Bad Request', 
-    401  => 'Unauthorized', 
-    402  => 'Payment Required', 
-    403  => 'Forbidden', 
-    404  => 'Not Found', 
-    405  => 'Method Not Allowed', 
-    406  => 'Not Acceptable', 
-    407  => 'Proxy Authentication Required', 
-    408  => 'Request Time-out', 
-    409  => 'Conflict', 
-    410  => 'Gone', 
-    411  => 'Length Required', 
-    412  => 'Precondition Failed', 
-    413  => 'Request Entity Too Large', 
-    414  => 'Request-URI Too Large', 
-    415  => 'Unsupported Media Type', 
-    500  => 'Internal Server Error', 
-    501  => 'Not Implemented', 
-    502  => 'Bad Gateway', 
-    503  => 'Service Unavailable', 
-    504  => 'Gateway Time-out', 
-    505  => 'HTTP Version not supported'
-  }
-
-  # Frequently used constants when constructing requests or responses.  Many times
-  # the constant just refers to a string with the same contents.  Using these constants
-  # gave about a 3% to 10% performance improvement over using the strings directly.
-  # Symbols did not really improve things much compared to constants.
-  #
-  # While Mongrel does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
-  # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or 
-  # too taxing on performance.
-  module Const
-    DATE = "Date".freeze
-
-    # This is the part of the path after the SCRIPT_NAME.  URIClassifier will determine this.
-    PATH_INFO="PATH_INFO".freeze
-
-    # This is the initial part that your handler is identified as by URIClassifier.
-    SCRIPT_NAME="SCRIPT_NAME".freeze
-
-    # The original URI requested by the client.  Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
-    REQUEST_URI='REQUEST_URI'.freeze
-    REQUEST_PATH='REQUEST_PATH'.freeze
-
-    MONGREL_VERSION="1.1.5".freeze
-
-    MONGREL_TMP_BASE="mongrel".freeze
-
-    # The standard empty 404 response for bad requests.  Use Error4040Handler for custom stuff.
-    ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
-
-    CONTENT_LENGTH="CONTENT_LENGTH".freeze
-
-    # A common header for indicating the server is too busy.  Not used yet.
-    ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
-
-    # The basic max request size we'll try to read.
-    CHUNK_SIZE=(16 * 1024)
-
-    # This is the maximum header that is allowed before a client is booted.  The parser detects
-    # this, but we'd also like to do this as well.
-    MAX_HEADER=1024 * (80 + 32)
-
-    # Maximum request body size before it is moved out of memory and into a tempfile for reading.
-    MAX_BODY=MAX_HEADER
-
-    # A frozen format for this is about 15% faster
-    STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n".freeze
-    CONTENT_TYPE = "Content-Type".freeze
-    LAST_MODIFIED = "Last-Modified".freeze
-    ETAG = "ETag".freeze
-    SLASH = "/".freeze
-    REQUEST_METHOD="REQUEST_METHOD".freeze
-    GET="GET".freeze
-    HEAD="HEAD".freeze
-    # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
-    ETAG_FORMAT="\"%x-%x-%x\"".freeze
-    HEADER_FORMAT="%s: %s\r\n".freeze
-    LINE_END="\r\n".freeze
-    REMOTE_ADDR="REMOTE_ADDR".freeze
-    HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
-    HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE".freeze
-    HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze
-    REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze
-    HOST = "HOST".freeze
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/debug.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/debug.rb
deleted file mode 100644 (file)
index 2686c1e..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'logger'
-require 'set'
-require 'socket'
-require 'fileutils'
-
-module MongrelDbg
-  SETTINGS = { :tracing => {}}
-  LOGGING = { }
-
-  def MongrelDbg::configure(log_dir = File.join("log","mongrel_debug"))
-    FileUtils.mkdir_p(log_dir)
-    @log_dir = log_dir
-    $objects_out=open(File.join("log","mongrel_debug","objects.log"),"w")
-    $objects_out.puts "run,classname,last,count,delta,lenmean,lensd,lenmax"
-    $objects_out.sync = true
-    $last_stat = nil
-    $run_count = 0
-  end
-
-  
-  def MongrelDbg::trace(target, message)
-    if SETTINGS[:tracing][target] and LOGGING[target]
-      LOGGING[target].log(Logger::DEBUG, message)
-    end
-  end
-
-  def MongrelDbg::begin_trace(target)
-    SETTINGS[:tracing][target] = true
-    if not LOGGING[target]
-      LOGGING[target] = Logger.new(File.join(@log_dir, "#{target.to_s}.log"))
-    end                          
-    MongrelDbg::trace(target, "TRACING ON #{Time.now}")
-  end
-
-  def MongrelDbg::end_trace(target)
-    SETTINGS[:tracing][target] = false
-    MongrelDbg::trace(target, "TRACING OFF #{Time.now}")
-    LOGGING[target].close
-    LOGGING[target] = nil
-  end
-
-  def MongrelDbg::tracing?(target)
-    SETTINGS[:tracing][target]
-  end
-end
-
-
-
-$open_files = {}
-
-class IO
-  alias_method :orig_open, :open
-  alias_method :orig_close, :close
-
-  def open(*arg, &blk)
-    $open_files[self] = args.inspect
-    orig_open(*arg,&blk)
-  end
-
-  def close(*arg,&blk)
-    $open_files.delete self
-    orig_close(*arg,&blk)
-  end
-end
-
-
-module Kernel
-  alias_method :orig_open, :open
-
-  def open(*arg, &blk)
-    $open_files[self] = arg[0]
-    orig_open(*arg,&blk)
-  end
-
-  def log_open_files
-    open_counts = {}
-    $open_files.each do |f,args|
-      open_counts[args] ||= 0
-      open_counts[args] += 1
-    end
-    MongrelDbg::trace(:files, open_counts.to_yaml)
-  end
-end  
-
-
-
-module RequestLog
-
-  # Just logs whatever requests it gets to STDERR (which ends up in the mongrel
-  # log when daemonized).
-  class Access < GemPlugin::Plugin "/handlers"
-    include Mongrel::HttpHandlerPlugin
-    
-    def process(request,response)
-      p = request.params
-      STDERR.puts "#{p['REMOTE_ADDR']} - [#{Time.now.httpdate}] \"#{p['REQUEST_METHOD']} #{p["REQUEST_URI"]} HTTP/1.1\""
-    end
-  end
-  
-
-  class Files < GemPlugin::Plugin "/handlers"
-    include Mongrel::HttpHandlerPlugin
-    
-    def process(request, response)
-      MongrelDbg::trace(:files, "#{Time.now} FILES OPEN BEFORE REQUEST #{request.params['PATH_INFO']}")
-      log_open_files
-    end
-    
-  end
-
-  # stolen from Robert Klemme
-  class Objects < GemPlugin::Plugin "/handlers"
-    include Mongrel::HttpHandlerPlugin
-
-    def process(request,response)
-      begin
-        stats = Hash.new(0)
-        lengths = {}
-        begin
-          ObjectSpace.each_object do |o| 
-            begin
-              if o.respond_to? :length
-                len = o.length
-                lengths[o.class] ||= Mongrel::Stats.new(o.class)
-                lengths[o.class].sample(len)
-              end
-            rescue Object
-            end
-  
-            stats[o.class] += 1
-          end
-        rescue Object # Ignore since ObjectSpace might not be loaded on JRuby
-        end
-
-        stats.sort {|(k1,v1),(k2,v2)| v2 <=> v1}.each do |k,v|
-          if $last_stat
-            delta = v - $last_stat[k]
-            if v > 10 and delta != 0
-              if lengths[k]
-                $objects_out.printf "%d,%s,%d,%d,%d,%f,%f,%f\n", $run_count, k, $last_stat[k], v, delta,lengths[k].mean,lengths[k].sd,lengths[k].max
-              else
-                $objects_out.printf "%d,%s,%d,%d,%d,,,\n", $run_count, k, $last_stat[k], v, delta
-              end
-            end
-          end
-        end
-
-        $run_count += 1
-        $last_stat = stats
-      rescue Object
-        STDERR.puts "object.log ERROR: #$!"
-      end
-    end
-  end
-
-  class Params < GemPlugin::Plugin "/handlers"
-    include Mongrel::HttpHandlerPlugin
-
-    def process(request, response)
-      MongrelDbg::trace(:rails, "#{Time.now} REQUEST #{request.params['PATH_INFO']}")
-      MongrelDbg::trace(:rails, request.params.to_yaml)
-    end
-
-  end
-
-  class Threads < GemPlugin::Plugin "/handlers"
-    include Mongrel::HttpHandlerPlugin
-
-    def process(request, response)
-      MongrelDbg::trace(:threads, "#{Time.now} REQUEST #{request.params['PATH_INFO']}")
-      begin
-        ObjectSpace.each_object do |obj|
-          begin
-            if obj.class == Mongrel::HttpServer
-              worker_list = obj.workers.list
-  
-              if worker_list.length > 0
-                keys = "-----\n\tKEYS:"
-                worker_list.each {|t| keys << "\n\t\t-- #{t}: #{t.keys.inspect}" }
-              end
-  
-              MongrelDbg::trace(:threads, "#{obj.host}:#{obj.port} -- THREADS: #{worker_list.length} #{keys}")
-            end
-          rescue Object # Ignore since obj.class can sometimes take parameters            
-          end
-        end
-      rescue Object # Ignore since ObjectSpace might not be loaded on JRuby
-      end
-    end
-  end
-end
-
-
-END {
-  MongrelDbg::trace(:files, "FILES OPEN AT EXIT")
-  log_open_files
-}
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/gems.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/gems.rb
deleted file mode 100644 (file)
index eb6d0d6..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-module Mongrel
-  module Gems
-    class << self
-    
-      def require(library, version = nil)
-        begin
-          Kernel.require library
-        rescue LoadError, RuntimeError => e
-          begin 
-            # ActiveSupport breaks 'require' by making it always return a true value
-            Kernel.require 'rubygems'
-            version ? gem(library, version) : gem(library)
-            retry
-          rescue Gem::LoadError, LoadError, RuntimeError
-            # puts "** #{library.inspect} could not be loaded" unless library == "mongrel_experimental"
-          end
-        end  
-      end
-      
-    end    
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/handlers.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/handlers.rb
deleted file mode 100644 (file)
index e643025..0000000
+++ /dev/null
@@ -1,468 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'mongrel/stats'
-require 'zlib'
-require 'yaml'
-
-module Mongrel
-
-  # You implement your application handler with this.  It's very light giving
-  # just the minimum necessary for you to handle a request and shoot back 
-  # a response.  Look at the HttpRequest and HttpResponse objects for how
-  # to use them.
-  #
-  # This is used for very simple handlers that don't require much to operate.
-  # More extensive plugins or those you intend to distribute as GemPlugins 
-  # should be implemented using the HttpHandlerPlugin mixin.
-  #
-  class HttpHandler
-    attr_reader :request_notify
-    attr_accessor :listener
-
-    # This will be called by Mongrel if HttpHandler.request_notify set to *true*.
-    # You only get the parameters for the request, with the idea that you'd "bound"
-    # the beginning of the request processing and the first call to process.
-    def request_begins(params)
-    end
-
-    # Called by Mongrel for each IO chunk that is received on the request socket
-    # from the client, allowing you to track the progress of the IO and monitor
-    # the input.  This will be called by Mongrel only if HttpHandler.request_notify
-    # set to *true*.
-    def request_progress(params, clen, total)
-    end
-
-    def process(request, response)
-    end
-
-  end
-
-
-  # This is used when your handler is implemented as a GemPlugin.
-  # The plugin always takes an options hash which you can modify
-  # and then access later.  They are stored by default for 
-  # the process method later.
-  module HttpHandlerPlugin
-    attr_reader :options
-    attr_reader :request_notify
-    attr_accessor :listener
-
-    def request_begins(params)
-    end
-
-    def request_progress(params, clen, total)
-    end
-
-    def initialize(options={})
-      @options = options
-      @header_only = false
-    end
-
-    def process(request, response)
-    end
-
-  end
-
-
-  #
-  # The server normally returns a 404 response if an unknown URI is requested, but it
-  # also returns a lame empty message.  This lets you do a 404 response
-  # with a custom message for special URIs.
-  #
-  class Error404Handler < HttpHandler
-
-    # Sets the message to return.  This is constructed once for the handler
-    # so it's pretty efficient.
-    def initialize(msg)
-      @response = Const::ERROR_404_RESPONSE + msg
-    end
-
-    # Just kicks back the standard 404 response with your special message.
-    def process(request, response)
-      response.socket.write(@response)
-    end
-
-  end
-
-  #
-  # Serves the contents of a directory.  You give it the path to the root
-  # where the files are located, and it tries to find the files based on 
-  # the PATH_INFO inside the directory.  If the requested path is a
-  # directory then it returns a simple directory listing.
-  #
-  # It does a simple protection against going outside it's root path by
-  # converting all paths to an absolute expanded path, and then making 
-  # sure that the final expanded path includes the root path.  If it doesn't
-  # than it simply gives a 404.
-  #
-  # If you pass nil as the root path, it will not check any locations or
-  # expand any paths. This lets you serve files from multiple drives
-  # on win32. It should probably not be used in a public-facing way
-  # without additional checks.
-  #
-  # The default content type is "text/plain; charset=ISO-8859-1" but you
-  # can change it anything you want using the DirHandler.default_content_type
-  # attribute.
-  #
-  class DirHandler < HttpHandler
-    attr_accessor :default_content_type
-    attr_reader :path
-
-    MIME_TYPES_FILE = "mime_types.yml"
-    MIME_TYPES = YAML.load_file(File.join(File.dirname(__FILE__), MIME_TYPES_FILE))
-
-    ONLY_HEAD_GET="Only HEAD and GET allowed.".freeze
-
-    # You give it the path to the directory root and and optional listing_allowed and index_html
-    def initialize(path, listing_allowed=true, index_html="index.html")
-      @path = File.expand_path(path) if path
-      @listing_allowed = listing_allowed
-      @index_html = index_html
-      @default_content_type = "application/octet-stream".freeze
-    end
-
-    # Checks if the given path can be served and returns the full path (or nil if not).
-    def can_serve(path_info)
-
-      req_path = HttpRequest.unescape(path_info)
-      # Add the drive letter or root path
-      req_path = File.join(@path, req_path) if @path
-      req_path = File.expand_path req_path
-      
-      if File.exist? req_path and (!@path or req_path.index(@path) == 0)
-        # It exists and it's in the right location
-        if File.directory? req_path
-          # The request is for a directory
-          index = File.join(req_path, @index_html)
-          if File.exist? index
-            # Serve the index
-            return index
-          elsif @listing_allowed
-            # Serve the directory
-            return req_path
-          else
-            # Do not serve anything
-            return nil
-          end
-        else
-          # It's a file and it's there
-          return req_path
-        end
-      else
-        # does not exist or isn't in the right spot
-        return nil
-      end
-    end
-
-
-    # Returns a simplistic directory listing if they're enabled, otherwise a 403.
-    # Base is the base URI from the REQUEST_URI, dir is the directory to serve 
-    # on the file system (comes from can_serve()), and response is the HttpResponse
-    # object to send the results on.
-    def send_dir_listing(base, dir, response)
-      # take off any trailing / so the links come out right
-      base = HttpRequest.unescape(base)
-      base.chop! if base[-1] == "/"[-1]
-
-      if @listing_allowed
-        response.start(200) do |head,out|
-          head[Const::CONTENT_TYPE] = "text/html"
-          out << "<html><head><title>Directory Listing</title></head><body>"
-          Dir.entries(dir).each do |child|
-            next if child == "."
-            out << "<a href=\"#{base}/#{ HttpRequest.escape(child)}\">"
-            out << (child == ".." ? "Up to parent.." : child)
-            out << "</a><br/>"
-          end
-          out << "</body></html>"
-        end
-      else
-        response.start(403) do |head,out|
-          out.write("Directory listings not allowed")
-        end
-      end
-    end
-
-
-    # Sends the contents of a file back to the user. Not terribly efficient since it's
-    # opening and closing the file for each read.
-    def send_file(req_path, request, response, header_only=false)
-
-      stat = File.stat(req_path)
-
-      # Set the last modified times as well and etag for all files
-      mtime = stat.mtime
-      # Calculated the same as apache, not sure how well the works on win32
-      etag = Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
-
-      modified_since = request.params[Const::HTTP_IF_MODIFIED_SINCE]
-      none_match = request.params[Const::HTTP_IF_NONE_MATCH]
-
-      # test to see if this is a conditional request, and test if
-      # the response would be identical to the last response
-      same_response = case
-                      when modified_since && !last_response_time = Time.httpdate(modified_since) rescue nil : false
-                      when modified_since && last_response_time > Time.now                                  : false
-                      when modified_since && mtime > last_response_time                                     : false
-                      when none_match     && none_match == '*'                                              : false
-                      when none_match     && !none_match.strip.split(/\s*,\s*/).include?(etag)              : false
-                      else modified_since || none_match  # validation successful if we get this far and at least one of the header exists
-                      end
-
-      header = response.header
-      header[Const::ETAG] = etag
-
-      if same_response
-        response.start(304) {}
-      else
-        
-        # First we setup the headers and status then we do a very fast send on the socket directly
-        
-        # Support custom responses except 404, which is the default. A little awkward. 
-        response.status = 200 if response.status == 404        
-        header[Const::LAST_MODIFIED] = mtime.httpdate
-
-        # Set the mime type from our map based on the ending
-        dot_at = req_path.rindex('.')
-        if dot_at
-          header[Const::CONTENT_TYPE] = MIME_TYPES[req_path[dot_at .. -1]] || @default_content_type
-        else
-          header[Const::CONTENT_TYPE] = @default_content_type
-        end
-
-        # send a status with out content length
-        response.send_status(stat.size)
-        response.send_header
-
-        if not header_only
-          response.send_file(req_path, stat.size < Const::CHUNK_SIZE * 2)
-        end
-      end
-    end
-
-    # Process the request to either serve a file or a directory listing
-    # if allowed (based on the listing_allowed parameter to the constructor).
-    def process(request, response)
-      req_method = request.params[Const::REQUEST_METHOD] || Const::GET
-      req_path = can_serve request.params[Const::PATH_INFO]
-      if not req_path
-        # not found, return a 404
-        response.start(404) do |head,out|
-          out << "File not found"
-        end
-      else
-        begin
-          if File.directory? req_path
-            send_dir_listing(request.params[Const::REQUEST_URI], req_path, response)
-          elsif req_method == Const::HEAD
-            send_file(req_path, request, response, true)
-          elsif req_method == Const::GET
-            send_file(req_path, request, response, false)
-          else
-            response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
-          end
-        rescue => details
-          STDERR.puts "Error sending file #{req_path}: #{details}"
-        end
-      end
-    end
-
-    # There is a small number of default mime types for extensions, but
-    # this lets you add any others you'll need when serving content.
-    def DirHandler::add_mime_type(extension, type)
-      MIME_TYPES[extension] = type
-    end
-
-  end
-
-
-  # When added to a config script (-S in mongrel_rails) it will
-  # look at the client's allowed response types and then gzip 
-  # compress anything that is going out.
-  #
-  # Valid option is :always_deflate => false which tells the handler to
-  # deflate everything even if the client can't handle it.
-  class DeflateFilter < HttpHandler
-    include Zlib
-    HTTP_ACCEPT_ENCODING = "HTTP_ACCEPT_ENCODING" 
-
-    def initialize(ops={})
-      @options = ops
-      @always_deflate = ops[:always_deflate] || false
-    end
-
-    def process(request, response)
-      accepts = request.params[HTTP_ACCEPT_ENCODING]
-      # only process if they support compression
-      if @always_deflate or (accepts and (accepts.include? "deflate" and not response.body_sent))
-        response.header["Content-Encoding"] = "deflate"
-        response.body = deflate(response.body)
-      end
-    end
-
-    private
-      def deflate(stream)
-        deflater = Deflate.new(
-          DEFAULT_COMPRESSION,
-          # drop the zlib header which causes both Safari and IE to choke
-          -MAX_WBITS, 
-          DEF_MEM_LEVEL,
-          DEFAULT_STRATEGY)
-
-        stream.rewind
-        gzout = StringIO.new(deflater.deflate(stream.read, FINISH))
-        stream.close
-        gzout.rewind
-        gzout
-      end
-  end
-
-
-  # Implements a few basic statistics for a particular URI.  Register it anywhere
-  # you want in the request chain and it'll quickly gather some numbers for you
-  # to analyze.  It is pretty fast, but don't put it out in production.
-  #
-  # You should pass the filter to StatusHandler as StatusHandler.new(:stats_filter => stats).
-  # This lets you then hit the status URI you want and get these stats from a browser.
-  #
-  # StatisticsFilter takes an option of :sample_rate.  This is a number that's passed to
-  # rand and if that number gets hit then a sample is taken.  This helps reduce the load
-  # and keeps the statistics valid (since sampling is a part of how they work).
-  #
-  # The exception to :sample_rate is that inter-request time is sampled on every request.
-  # If this wasn't done then it wouldn't be accurate as a measure of time between requests.
-  class StatisticsFilter < HttpHandler
-    attr_reader :stats
-
-    def initialize(ops={})
-      @sample_rate = ops[:sample_rate] || 300
-
-      @processors = Mongrel::Stats.new("processors")
-      @reqsize = Mongrel::Stats.new("request Kb")
-      @headcount = Mongrel::Stats.new("req param count")
-      @respsize = Mongrel::Stats.new("response Kb")
-      @interreq = Mongrel::Stats.new("inter-request time")
-    end
-
-
-    def process(request, response)
-      if rand(@sample_rate)+1 == @sample_rate
-        @processors.sample(listener.workers.list.length)
-        @headcount.sample(request.params.length)
-        @reqsize.sample(request.body.length / 1024.0)
-        @respsize.sample((response.body.length + response.header.out.length) / 1024.0)
-      end
-      @interreq.tick
-    end
-
-    def dump
-      "#{@processors.to_s}\n#{@reqsize.to_s}\n#{@headcount.to_s}\n#{@respsize.to_s}\n#{@interreq.to_s}"
-    end
-  end
-
-
-  # The :stats_filter is basically any configured stats filter that you've added to this same
-  # URI.  This lets the status handler print out statistics on how Mongrel is doing.
-  class StatusHandler < HttpHandler
-    def initialize(ops={})
-      @stats = ops[:stats_filter]
-    end
-
-    def table(title, rows)
-      results = "<table border=\"1\"><tr><th colspan=\"#{rows[0].length}\">#{title}</th></tr>"
-      rows.each do |cols|
-        results << "<tr>"
-        cols.each {|col| results << "<td>#{col}</td>" }
-        results << "</tr>"
-      end
-      results + "</table>"
-    end
-
-    def describe_listener
-      results = ""
-      results << "<h1>Listener #{listener.host}:#{listener.port}</h1>"
-      results << table("settings", [
-                       ["host",listener.host],
-                       ["port",listener.port],
-                       ["throttle",listener.throttle],
-                       ["timeout",listener.timeout],
-                       ["workers max",listener.num_processors],
-      ])
-
-      if @stats
-        results << "<h2>Statistics</h2><p>N means the number of samples, pay attention to MEAN, SD, MIN and MAX."
-        results << "<pre>#{@stats.dump}</pre>"
-      end
-
-      results << "<h2>Registered Handlers</h2>"
-      handler_map = listener.classifier.handler_map
-      results << table("handlers", handler_map.map {|uri,handlers| 
-        [uri, 
-            "<pre>" + 
-            handlers.map {|h| h.class.to_s }.join("\n") + 
-            "</pre>"
-        ]
-      })
-
-      results
-    end
-
-    def process(request, response)
-      response.start do |head,out|
-        out.write <<-END
-        <html><body><title>Mongrel Server Status</title>
-        #{describe_listener}
-        </body></html>
-        END
-      end
-    end
-  end
-
-  # This handler allows you to redirect one url to another.
-  # You can use it like String#gsub, where the string is the REQUEST_URI.
-  # REQUEST_URI is the full path with GET parameters.
-  #
-  # Eg. /test/something?help=true&disclaimer=false
-  #
-  # == Examples
-  #
-  #   h = Mongrel::HttpServer.new('0.0.0.0')
-  #   h.register '/test', Mongrel::RedirectHandler.new('/to/there') # simple
-  #   h.register '/to',   Mongrel::RedirectHandler.new(/t/, 'w') # regexp
-  #   # and with a block
-  #   h.register '/hey',  Mongrel::RedirectHandler.new(/(\w+)/) { |match| ... }
-  # 
-  class RedirectHandler < Mongrel::HttpHandler
-    # You set the rewrite rules when building the object.
-    #
-    # pattern            => What to look for or replacement if used alone
-    #
-    # replacement, block => One of them is used to replace the found text
-
-    def initialize(pattern, replacement = nil, &block)
-      unless replacement or block
-        @pattern, @replacement = nil, pattern
-      else
-        @pattern, @replacement, @block = pattern, replacement, block
-      end
-    end
-
-    # Process the request and return a redirect response
-    def process(request, response)
-      unless @pattern
-        response.socket.write(Mongrel::Const::REDIRECT % @replacement)
-      else
-        if @block
-          new_path = request.params['REQUEST_URI'].gsub(@pattern, &@block)
-        else
-          new_path = request.params['REQUEST_URI'].gsub(@pattern, @replacement)
-        end
-        response.socket.write(Mongrel::Const::REDIRECT % new_path)
-      end
-    end
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/header_out.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/header_out.rb
deleted file mode 100644 (file)
index b34e95e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-module Mongrel
-  # This class implements a simple way of constructing the HTTP headers dynamically
-  # via a Hash syntax.  Think of it as a write-only Hash.  Refer to HttpResponse for
-  # information on how this is used.
-  #
-  # One consequence of this write-only nature is that you can write multiple headers
-  # by just doing them twice (which is sometimes needed in HTTP), but that the normal
-  # semantics for Hash (where doing an insert replaces) is not there.
-  class HeaderOut
-    attr_reader :out
-    attr_accessor :allowed_duplicates
-
-    def initialize(out)
-      @sent = {}
-      @allowed_duplicates = {"Set-Cookie" => true, "Set-Cookie2" => true,
-        "Warning" => true, "WWW-Authenticate" => true}
-      @out = out
-    end
-
-    # Simply writes "#{key}: #{value}" to an output buffer.
-    def[]=(key,value)
-      if not @sent.has_key?(key) or @allowed_duplicates.has_key?(key)
-        @sent[key] = true
-        @out.write(Const::HEADER_FORMAT % [key, value])
-      end
-    end
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_request.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_request.rb
deleted file mode 100644 (file)
index 82ffe42..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-
-module Mongrel
-  #
-  # When a handler is found for a registered URI then this class is constructed
-  # and passed to your HttpHandler::process method.  You should assume that 
-  # *one* handler processes all requests.  Included in the HttpRequest is a
-  # HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
-  # which is a string containing the request body (raw for now).
-  #
-  # The HttpRequest.initialize method will convert any request that is larger than
-  # Const::MAX_BODY into a Tempfile and use that as the body.  Otherwise it uses 
-  # a StringIO object.  To be safe, you should assume it works like a file.
-  #
-  # The HttpHandler.request_notify system is implemented by having HttpRequest call
-  # HttpHandler.request_begins, HttpHandler.request_progress, HttpHandler.process during
-  # the IO processing.  This adds a small amount of overhead but lets you implement
-  # finer controlled handlers and filters.
-  #
-  class HttpRequest
-    attr_reader :body, :params
-
-    # You don't really call this.  It's made for you.
-    # Main thing it does is hook up the params, and store any remaining
-    # body data into the HttpRequest.body attribute.
-    def initialize(params, socket, dispatchers)
-      @params = params
-      @socket = socket
-      @dispatchers = dispatchers
-      content_length = @params[Const::CONTENT_LENGTH].to_i
-      remain = content_length - @params.http_body.length
-      
-      # tell all dispatchers the request has begun
-      @dispatchers.each do |dispatcher|
-        dispatcher.request_begins(@params) 
-      end unless @dispatchers.nil? || @dispatchers.empty?
-
-      # Some clients (like FF1.0) report 0 for body and then send a body.  This will probably truncate them but at least the request goes through usually.
-      if remain <= 0
-        # we've got everything, pack it up
-        @body = StringIO.new
-        @body.write @params.http_body
-        update_request_progress(0, content_length)
-      elsif remain > 0
-        # must read more data to complete body
-        if remain > Const::MAX_BODY
-          # huge body, put it in a tempfile
-          @body = Tempfile.new(Const::MONGREL_TMP_BASE)
-          @body.binmode
-        else
-          # small body, just use that
-          @body = StringIO.new 
-        end
-
-        @body.write @params.http_body
-        read_body(remain, content_length)
-      end
-
-      @body.rewind if @body
-    end
-
-    # updates all dispatchers about our progress
-    def update_request_progress(clen, total)
-      return if @dispatchers.nil? || @dispatchers.empty?
-      @dispatchers.each do |dispatcher|
-        dispatcher.request_progress(@params, clen, total) 
-      end 
-    end
-    private :update_request_progress
-
-    # Does the heavy lifting of properly reading the larger body requests in 
-    # small chunks.  It expects @body to be an IO object, @socket to be valid,
-    # and will set @body = nil if the request fails.  It also expects any initial
-    # part of the body that has been read to be in the @body already.
-    def read_body(remain, total)
-      begin
-        # write the odd sized chunk first
-        @params.http_body = read_socket(remain % Const::CHUNK_SIZE)
-
-        remain -= @body.write(@params.http_body)
-
-        update_request_progress(remain, total)
-
-        # then stream out nothing but perfectly sized chunks
-        until remain <= 0 or @socket.closed?
-          # ASSUME: we are writing to a disk and these writes always write the requested amount
-          @params.http_body = read_socket(Const::CHUNK_SIZE)
-          remain -= @body.write(@params.http_body)
-
-          update_request_progress(remain, total)
-        end
-      rescue Object => e
-        STDERR.puts "#{Time.now}: Error reading HTTP body: #{e.inspect}"
-        STDERR.puts e.backtrace.join("\n")
-        # any errors means we should delete the file, including if the file is dumped
-        @socket.close rescue nil
-        @body.delete if @body.class == Tempfile
-        @body = nil # signals that there was a problem
-      end
-    end
-    def read_socket(len)
-      if !@socket.closed?
-        data = @socket.read(len)
-        if !data
-          raise "Socket read return nil"
-        elsif data.length != len
-          raise "Socket read returned insufficient data: #{data.length}"
-        else
-          data
-        end
-      else
-        raise "Socket already closed when reading."
-      end
-    end
-
-    # Performs URI escaping so that you can construct proper
-    # query strings faster.  Use this rather than the cgi.rb
-    # version since it's faster.  (Stolen from Camping).
-    def self.escape(s)
-      s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
-        '%'+$1.unpack('H2'*$1.size).join('%').upcase
-      }.tr(' ', '+') 
-    end
-
-
-    # Unescapes a URI escaped string. (Stolen from Camping).
-    def self.unescape(s)
-      s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
-        [$1.delete('%')].pack('H*')
-      } 
-    end
-
-    # Parses a query string by breaking it up at the '&' 
-    # and ';' characters.  You can also use this to parse
-    # cookies by changing the characters used in the second
-    # parameter (which defaults to '&;'.
-    def self.query_parse(qs, d = '&;')
-      params = {}
-      (qs||'').split(/[#{d}] */n).inject(params) { |h,p|
-        k, v=unescape(p).split('=',2)
-        if cur = params[k]
-          if cur.class == Array
-            params[k] << v
-          else
-            params[k] = [cur, v]
-          end
-        else
-          params[k] = v
-        end
-      }
-
-      return params
-    end
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_response.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/http_response.rb
deleted file mode 100644 (file)
index 32e433e..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-module Mongrel
-  # Writes and controls your response to the client using the HTTP/1.1 specification.
-  # You use it by simply doing:
-  #
-  #  response.start(200) do |head,out|
-  #    head['Content-Type'] = 'text/plain'
-  #    out.write("hello\n")
-  #  end
-  #
-  # The parameter to start is the response code--which Mongrel will translate for you
-  # based on HTTP_STATUS_CODES.  The head parameter is how you write custom headers.
-  # The out parameter is where you write your body.  The default status code for 
-  # HttpResponse.start is 200 so the above example is redundant.
-  # 
-  # As you can see, it's just like using a Hash and as you do this it writes the proper
-  # header to the output on the fly.  You can even intermix specifying headers and 
-  # writing content.  The HttpResponse class with write the things in the proper order
-  # once the HttpResponse.block is ended.
-  #
-  # You may also work the HttpResponse object directly using the various attributes available
-  # for the raw socket, body, header, and status codes.  If you do this you're on your own.
-  # A design decision was made to force the client to not pipeline requests.  HTTP/1.1 
-  # pipelining really kills the performance due to how it has to be handled and how 
-  # unclear the standard is.  To fix this the HttpResponse gives a "Connection: close"
-  # header which forces the client to close right away.  The bonus for this is that it
-  # gives a pretty nice speed boost to most clients since they can close their connection
-  # immediately.
-  #
-  # One additional caveat is that you don't have to specify the Content-length header
-  # as the HttpResponse will write this for you based on the out length.
-  class HttpResponse
-    attr_reader :socket
-    attr_reader :body
-    attr_writer :body
-    attr_reader :header
-    attr_reader :status
-    attr_writer :status
-    attr_reader :body_sent
-    attr_reader :header_sent
-    attr_reader :status_sent
-
-    def initialize(socket)
-      @socket = socket
-      @body = StringIO.new
-      @status = 404
-      @reason = nil
-      @header = HeaderOut.new(StringIO.new)
-      @header[Const::DATE] = Time.now.httpdate
-      @body_sent = false
-      @header_sent = false
-      @status_sent = false
-    end
-
-    # Receives a block passing it the header and body for you to work with.
-    # When the block is finished it writes everything you've done to 
-    # the socket in the proper order.  This lets you intermix header and
-    # body content as needed.  Handlers are able to modify pretty much
-    # any part of the request in the chain, and can stop further processing
-    # by simple passing "finalize=true" to the start method.  By default
-    # all handlers run and then mongrel finalizes the request when they're
-    # all done.
-    def start(status=200, finalize=false, reason=nil)
-      @status = status.to_i
-      @reason = reason
-      yield @header, @body
-      finished if finalize
-    end
-
-    # Primarily used in exception handling to reset the response output in order to write
-    # an alternative response.  It will abort with an exception if you have already
-    # sent the header or the body.  This is pretty catastrophic actually.
-    def reset
-      if @body_sent
-        raise "You have already sent the request body."
-      elsif @header_sent
-        raise "You have already sent the request headers."
-      else
-        @header.out.truncate(0)
-        @body.close
-        @body = StringIO.new
-      end
-    end
-
-    def send_status(content_length=@body.length)
-      if not @status_sent
-        @header['Content-Length'] = content_length if content_length and @status != 304
-        write(Const::STATUS_FORMAT % [@status, @reason || HTTP_STATUS_CODES[@status]])
-        @status_sent = true
-      end
-    end
-
-    def send_header
-      if not @header_sent
-        @header.out.rewind
-        write(@header.out.read + Const::LINE_END)
-        @header_sent = true
-      end
-    end
-
-    def send_body
-      if not @body_sent
-        @body.rewind
-        write(@body.read)
-        @body_sent = true
-      end
-    end 
-
-    # Appends the contents of +path+ to the response stream.  The file is opened for binary
-    # reading and written in chunks to the socket.
-    #
-    # Sendfile API support has been removed in 0.3.13.4 due to stability problems.
-    def send_file(path, small_file = false)
-      if small_file
-        File.open(path, "rb") {|f| @socket << f.read }
-      else
-        File.open(path, "rb") do |f|
-          while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
-            begin
-              write(chunk)
-            rescue Object => exc
-              break
-            end
-          end
-        end
-      end
-      @body_sent = true
-    end
-
-    def socket_error(details)
-      # ignore these since it means the client closed off early
-      @socket.close rescue nil
-      done = true
-      raise details
-    end
-
-    def write(data)
-      @socket.write(data)
-    rescue => details
-      socket_error(details)
-    end
-
-    # This takes whatever has been done to header and body and then writes it in the
-    # proper format to make an HTTP/1.1 response.
-    def finished
-      send_status
-      send_header
-      send_body
-    end
-
-    # Used during error conditions to mark the response as "done" so there isn't any more processing
-    # sent to the client.
-    def done=(val)
-      @status_sent = true
-      @header_sent = true
-      @body_sent = true
-    end
-
-    def done
-      (@status_sent and @header_sent and @body_sent)
-    end
-
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/init.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/init.rb
deleted file mode 100644 (file)
index 00911f4..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'mongrel/gems'
-Mongrel::Gems.require 'gem_plugin'
-
-# File is just a stub that makes sure the mongrel_plugins gem is loaded and ready
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/mime_types.yml b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/mime_types.yml
deleted file mode 100644 (file)
index 3ce1739..0000000
+++ /dev/null
@@ -1,616 +0,0 @@
----
-.a: application/octet-stream
-.abc: text/vnd.abc
-.acgi: text/html
-.afl: video/animaflex
-.ai: application/postscript
-.aif: audio/aiff
-.aifc: audio/aiff
-.aiff: audio/aiff
-.aip: text/x-audiosoft-intra
-.ani: application/x-navi-animation
-.aps: application/mime
-.arc: application/octet-stream
-.arj: application/octet-stream
-.art: image/x-jg
-.asf: video/x-ms-asf
-.asm: text/x-asm
-.asp: text/asp
-.asr: video/x-ms-asf
-.asx: video/x-ms-asf
-.atom: application/atom+xml
-.au: audio/basic
-.au: audio/x-au
-.avi: video/avi
-.avs: video/avs-video
-.axs: application/olescript
-.bas: text/plain
-.bcpio: application/x-bcpio
-.bin: application/octet-stream
-.bm: image/bmp
-.bmp: image/bmp
-.boo: application/book
-.book: application/book
-.boz: application/x-bzip2
-.bsh: application/x-bsh
-.bz2: application/x-bzip2
-.bz: application/x-bzip
-.c: text/plain
-.cat: application/octet-stream
-.cc: text/plain
-.ccad: application/clariscad
-.cco: application/x-cocoa
-.cdf: application/cdf
-.cer: application/x-x509-ca-cert
-.cha: application/x-chat
-.chat: application/x-chat
-.class: application/java
-.class: application/octet-stream
-.clp: application/x-msclip
-.cmx: image/x-cmx
-.cod: image/cis-cod
-.com: application/octet-stream
-.com: text/plain
-.conf: text/plain
-.cpio: application/x-cpio
-.cpp: text/x-c
-.cpt: application/x-cpt
-.crd: application/x-mscardfile
-.crl: application/pkcs-crl
-.crl: application/pkix-crl
-.crt: application/x-x509-ca-cert
-.csh: application/x-csh
-.csh: text/x-script.csh
-.css: text/css
-.cxx: text/plain
-.dcr: application/x-director
-.deb: application/octet-stream
-.deepv: application/x-deepv
-.def: text/plain
-.der: application/x-x509-ca-cert
-.dhh: application/david-heinemeier-hansson
-.dif: video/x-dv
-.dir: application/x-director
-.dl: video/dl
-.dll: application/octet-stream
-.dmg: application/octet-stream
-.dms: application/octet-stream
-.doc: application/msword
-.dp: application/commonground
-.drw: application/drafting
-.dump: application/octet-stream
-.dv: video/x-dv
-.dvi: application/x-dvi
-.dwg: application/acad
-.dwg: image/x-dwg
-.dxf: application/dxf
-.dxf: image/x-dwg
-.dxr: application/x-director
-.ear: application/java-archive
-.el: text/x-script.elisp
-.elc: application/x-bytecode.elisp (compiled elisp)
-.elc: application/x-elc
-.env: application/x-envoy
-.eot: application/octet-stream
-.eps: application/postscript
-.es: application/x-esrehber
-.etx: text/x-setext
-.evy: application/envoy
-.evy: application/x-envoy
-.exe: application/octet-stream
-.f77: text/x-fortran
-.f90: text/plain
-.f90: text/x-fortran
-.f: text/x-fortran
-.fdf: application/vnd.fdf
-.fif: application/fractals
-.fif: image/fif
-.fli: video/fli
-.fli: video/x-fli
-.flo: image/florian
-.flr: x-world/x-vrml
-.flv: video/x-flv
-.flx: text/vnd.fmi.flexstor
-.fmf: video/x-atomic3d-feature
-.for: text/plain
-.for: text/x-fortran
-.fpx: image/vnd.fpx
-.fpx: image/vnd.net-fpx
-.frl: application/freeloader
-.funk: audio/make
-.g3: image/g3fax
-.g: text/plain
-.gif: image/gif
-.gl: video/gl
-.gl: video/x-gl
-.gsd: audio/x-gsm
-.gsm: audio/x-gsm
-.gsp: application/x-gsp
-.gss: application/x-gss
-.gtar: application/x-gtar
-.gz: application/x-compressed
-.gzip: application/x-gzip
-.h: text/plain
-.hdf: application/x-hdf
-.help: application/x-helpfile
-.hgl: application/vnd.hp-hpgl
-.hh: text/plain
-.hlb: text/x-script
-.hlp: application/hlp
-.hpg: application/vnd.hp-hpgl
-.hpgl: application/vnd.hp-hpgl
-.hqx: application/binhex
-.hta: application/hta
-.htc: text/x-component
-.htm: text/html
-.html: text/html
-.htmls: text/html
-.htt: text/webviewhtml
-.htx: text/html
-.ico: image/x-icon
-.idc: text/plain
-.ief: image/ief
-.iefs: image/ief
-.iges: application/iges
-.igs: application/iges
-.iii: application/x-iphone
-.ima: application/x-ima
-.imap: application/x-httpd-imap
-.img: application/octet-stream
-.inf: application/inf
-.ins: application/x-internet-signup
-.ins: application/x-internett-signup
-.ip: application/x-ip2
-.iso: application/octet-stream
-.isp: application/x-internet-signup
-.isu: video/x-isvideo
-.it: audio/it
-.iv: application/x-inventor
-.ivr: i-world/i-vrml
-.ivy: application/x-livescreen
-.jam: audio/x-jam
-.jar: application/java-archive
-.jardiff: application/x-java-archive-diff
-.jav: text/plain
-.jav: text/x-java-source
-.java: text/plain
-.java: text/x-java-source
-.jcm: application/x-java-commerce
-.jfif-tbnl: image/jpeg
-.jfif: image/jpeg
-.jfif: image/pipeg
-.jfif: image/pjpeg
-.jng: image/x-jng
-.jnlp: application/x-java-jnlp-file
-.jpe: image/jpeg
-.jpeg: image/jpeg
-.jpg: image/jpeg
-.jps: image/x-jps
-.js: application/x-javascript
-.js: text/javascript
-.jut: image/jutvision
-.kar: audio/midi
-.kar: music/x-karaoke
-.ksh: application/x-ksh
-.ksh: text/x-script.ksh
-.la: audio/nspaudio
-.la: audio/x-nspaudio
-.lam: audio/x-liveaudio
-.latex: application/x-latex
-.lha: application/lha
-.lha: application/octet-stream
-.lha: application/x-lha
-.lhx: application/octet-stream
-.list: text/plain
-.lma: audio/nspaudio
-.lma: audio/x-nspaudio
-.log: text/plain
-.lsf: video/x-la-asf
-.lsp: application/x-lisp
-.lsp: text/x-script.lisp
-.lst: text/plain
-.lsx: text/x-la-asf
-.lsx: video/x-la-asf
-.ltx: application/x-latex
-.lzh: application/octet-stream
-.lzh: application/x-lzh
-.lzx: application/lzx
-.lzx: application/octet-stream
-.lzx: application/x-lzx
-.m13: application/x-msmediaview
-.m14: application/x-msmediaview
-.m1v: video/mpeg
-.m2a: audio/mpeg
-.m2v: video/mpeg
-.m3u: audio/x-mpegurl
-.m: text/x-m
-.man: application/x-troff-man
-.map: application/x-navimap
-.mar: text/plain
-.mbd: application/mbedlet
-.mc: application/x-magic-cap-package-1.0
-.mcd: application/mcad
-.mcd: application/x-mathcad
-.mcf: image/vasa
-.mcf: text/mcf
-.mcp: application/netmc
-.mdb: application/x-msaccess
-.me: application/x-troff-me
-.mht: message/rfc822
-.mhtml: message/rfc822
-.mid: audio/mid
-.mid: audio/midi
-.mid: audio/x-mid
-.mid: audio/x-midi
-.midi: audio/midi
-.midi: audio/x-mid
-.midi: audio/x-midi
-.mif: application/x-frame
-.mif: application/x-mif
-.mime: message/rfc822
-.mime: www/mime
-.mjf: audio/x-vnd.audioexplosion.mjuicemediafile
-.mjpg: video/x-motion-jpeg
-.mm: application/base64
-.mm: application/x-meme
-.mme: application/base64
-.mml: text/mathml
-.mng: video/x-mng
-.mod: audio/mod
-.moov: video/quicktime
-.mov: video/quicktime
-.movie: video/x-sgi-movie
-.mp2: audio/mpeg
-.mp3: audio/mpeg
-.mpa: audio/mpeg
-.mpc: application/x-project
-.mpe: video/mpeg
-.mpeg: video/mpeg
-.mpg: video/mpeg
-.mpga: audio/mpeg
-.mpp: application/vnd.ms-project
-.mpt: application/x-project
-.mpv2: video/mpeg
-.mpv: application/x-project
-.mpx: application/x-project
-.mrc: application/marc
-.ms: application/x-troff-ms
-.msi: application/octet-stream
-.msm: application/octet-stream
-.msp: application/octet-stream
-.mv: video/x-sgi-movie
-.mvb: application/x-msmediaview
-.my: audio/make
-.mzz: application/x-vnd.audioexplosion.mzz
-.nap: image/naplps
-.naplps: image/naplps
-.nc: application/x-netcdf
-.ncm: application/vnd.nokia.configuration-message
-.nif: image/x-niff
-.niff: image/x-niff
-.nix: application/x-mix-transfer
-.nsc: application/x-conference
-.nvd: application/x-navidoc
-.nws: message/rfc822
-.o: application/octet-stream
-.oda: application/oda
-.omc: application/x-omc
-.omcd: application/x-omcdatamaker
-.omcr: application/x-omcregerator
-.p10: application/pkcs10
-.p10: application/x-pkcs10
-.p12: application/pkcs-12
-.p12: application/x-pkcs12
-.p7a: application/x-pkcs7-signature
-.p7b: application/x-pkcs7-certificates
-.p7c: application/pkcs7-mime
-.p7c: application/x-pkcs7-mime
-.p7m: application/pkcs7-mime
-.p7m: application/x-pkcs7-mime
-.p7r: application/x-pkcs7-certreqresp
-.p7s: application/pkcs7-signature
-.p7s: application/x-pkcs7-signature
-.p: text/x-pascal
-.part: application/pro_eng
-.pas: text/pascal
-.pbm: image/x-portable-bitmap
-.pcl: application/vnd.hp-pcl
-.pcl: application/x-pcl
-.pct: image/x-pict
-.pcx: image/x-pcx
-.pdb: application/x-pilot
-.pdf: application/pdf
-.pem: application/x-x509-ca-cert
-.pfunk: audio/make
-.pfunk: audio/make.my.funk
-.pfx: application/x-pkcs12
-.pgm: image/x-portable-graymap
-.pgm: image/x-portable-greymap
-.pic: image/pict
-.pict: image/pict
-.pkg: application/x-newton-compatible-pkg
-.pko: application/vnd.ms-pki.pko
-.pko: application/ynd.ms-pkipko
-.pl: application/x-perl
-.pl: text/plain
-.pl: text/x-script.perl
-.plx: application/x-pixclscript
-.pm4: application/x-pagemaker
-.pm5: application/x-pagemaker
-.pm: application/x-perl
-.pm: image/x-xpixmap
-.pm: text/x-script.perl-module
-.pma: application/x-perfmon
-.pmc: application/x-perfmon
-.pml: application/x-perfmon
-.pmr: application/x-perfmon
-.pmw: application/x-perfmon
-.png: image/png
-.pnm: application/x-portable-anymap
-.pnm: image/x-portable-anymap
-.pot,: application/vnd.ms-powerpoint
-.pot: application/mspowerpoint
-.pot: application/vnd.ms-powerpoint
-.pov: model/x-pov
-.ppa: application/vnd.ms-powerpoint
-.ppm: image/x-portable-pixmap
-.pps: application/mspowerpoint
-.ppt: application/mspowerpoint
-.ppz: application/mspowerpoint
-.prc: application/x-pilot
-.pre: application/x-freelance
-.prf: application/pics-rules
-.prt: application/pro_eng
-.ps: application/postscript
-.psd: application/octet-stream
-.pub: application/x-mspublisher
-.pvu: paleovu/x-pv
-.pwz: application/vnd.ms-powerpoint
-.py: text/x-script.phyton
-.pyc: applicaiton/x-bytecode.python
-.qcp: audio/vnd.qcelp
-.qd3: x-world/x-3dmf
-.qd3d: x-world/x-3dmf
-.qif: image/x-quicktime
-.qt: video/quicktime
-.qtc: video/x-qtc
-.qti: image/x-quicktime
-.qtif: image/x-quicktime
-.ra: audio/x-pn-realaudio
-.ra: audio/x-pn-realaudio-plugin
-.ra: audio/x-realaudio
-.ram: audio/x-pn-realaudio
-.rar: application/x-rar-compressed
-.ras: application/x-cmu-raster
-.ras: image/cmu-raster
-.ras: image/x-cmu-raster
-.rast: image/cmu-raster
-.rexx: text/x-script.rexx
-.rf: image/vnd.rn-realflash
-.rgb: image/x-rgb
-.rm: application/vnd.rn-realmedia
-.rm: audio/x-pn-realaudio
-.rmi: audio/mid
-.rmm: audio/x-pn-realaudio
-.rmp: audio/x-pn-realaudio
-.rmp: audio/x-pn-realaudio-plugin
-.rng: application/ringing-tones
-.rng: application/vnd.nokia.ringing-tone
-.rnx: application/vnd.rn-realplayer
-.roff: application/x-troff
-.rp: image/vnd.rn-realpix
-.rpm: application/x-redhat-package-manager
-.rpm: audio/x-pn-realaudio-plugin
-.rss: text/xml
-.rt: text/richtext
-.rt: text/vnd.rn-realtext
-.rtf: application/rtf
-.rtf: application/x-rtf
-.rtf: text/richtext
-.rtx: application/rtf
-.rtx: text/richtext
-.run: application/x-makeself
-.rv: video/vnd.rn-realvideo
-.s3m: audio/s3m
-.s: text/x-asm
-.saveme: application/octet-stream
-.sbk: application/x-tbook
-.scd: application/x-msschedule
-.scm: application/x-lotusscreencam
-.scm: text/x-script.guile
-.scm: text/x-script.scheme
-.scm: video/x-scm
-.sct: text/scriptlet
-.sdml: text/plain
-.sdp: application/sdp
-.sdp: application/x-sdp
-.sdr: application/sounder
-.sea: application/sea
-.sea: application/x-sea
-.set: application/set
-.setpay: application/set-payment-initiation
-.setreg: application/set-registration-initiation
-.sgm: text/sgml
-.sgm: text/x-sgml
-.sgml: text/sgml
-.sgml: text/x-sgml
-.sh: application/x-bsh
-.sh: application/x-sh
-.sh: application/x-shar
-.sh: text/x-script.sh
-.shar: application/x-bsh
-.shar: application/x-shar
-.shtml: text/html
-.shtml: text/x-server-parsed-html
-.sid: audio/x-psid
-.sit: application/x-sit
-.sit: application/x-stuffit
-.skd: application/x-koan
-.skm: application/x-koan
-.skp: application/x-koan
-.skt: application/x-koan
-.sl: application/x-seelogo
-.smi: application/smil
-.smil: application/smil
-.snd: audio/basic
-.snd: audio/x-adpcm
-.sol: application/solids
-.spc: application/x-pkcs7-certificates
-.spc: text/x-speech
-.spl: application/futuresplash
-.spr: application/x-sprite
-.sprite: application/x-sprite
-.src: application/x-wais-source
-.ssi: text/x-server-parsed-html
-.ssm: application/streamingmedia
-.sst: application/vnd.ms-pki.certstore
-.sst: application/vnd.ms-pkicertstore
-.step: application/step
-.stl: application/sla
-.stl: application/vnd.ms-pki.stl
-.stl: application/vnd.ms-pkistl
-.stl: application/x-navistyle
-.stm: text/html
-.stp: application/step
-.sv4cpio: application/x-sv4cpio
-.sv4crc: application/x-sv4crc
-.svf: image/vnd.dwg
-.svf: image/x-dwg
-.svg: image/svg+xml
-.svr: application/x-world
-.svr: x-world/x-svr
-.swf: application/x-shockwave-flash
-.t: application/x-troff
-.talk: text/x-speech
-.tar: application/x-tar
-.tbk: application/toolbook
-.tbk: application/x-tbook
-.tcl: application/x-tcl
-.tcl: text/x-script.tcl
-.tcsh: text/x-script.tcsh
-.tex: application/x-tex
-.texi: application/x-texinfo
-.texinfo: application/x-texinfo
-.text: application/plain
-.text: text/plain
-.tgz: application/gnutar
-.tgz: application/x-compressed
-.tif: image/tiff
-.tiff: image/tiff
-.tk: application/x-tcl
-.tr: application/x-troff
-.trm: application/x-msterminal
-.tsi: audio/tsp-audio
-.tsp: application/dsptype
-.tsp: audio/tsplayer
-.tsv: text/tab-separated-values
-.turbot: image/florian
-.txt: text/plain
-.uil: text/x-uil
-.uls: text/iuls
-.uni: text/uri-list
-.unis: text/uri-list
-.unv: application/i-deas
-.uri: text/uri-list
-.uris: text/uri-list
-.ustar: application/x-ustar
-.ustar: multipart/x-ustar
-.uu: application/octet-stream
-.uu: text/x-uuencode
-.uue: text/x-uuencode
-.vcd: application/x-cdlink
-.vcf: text/x-vcard
-.vcs: text/x-vcalendar
-.vda: application/vda
-.vdo: video/vdo
-.vew: application/groupwise
-.viv: video/vivo
-.viv: video/vnd.vivo
-.vivo: video/vivo
-.vivo: video/vnd.vivo
-.vmd: application/vocaltec-media-desc
-.vmf: application/vocaltec-media-file
-.voc: audio/voc
-.voc: audio/x-voc
-.vos: video/vosaic
-.vox: audio/voxware
-.vqe: audio/x-twinvq-plugin
-.vqf: audio/x-twinvq
-.vql: audio/x-twinvq-plugin
-.vrml: application/x-vrml
-.vrml: model/vrml
-.vrml: x-world/x-vrml
-.vrt: x-world/x-vrt
-.vsd: application/x-visio
-.vst: application/x-visio
-.vsw: application/x-visio
-.w60: application/wordperfect6.0
-.w61: application/wordperfect6.1
-.w6w: application/msword
-.war: application/java-archive
-.wav: audio/wav
-.wav: audio/x-wav
-.wb1: application/x-qpro
-.wbmp: image/vnd.wap.wbmp
-.wbmp: image/vnd.wap.wbmp
-.wcm: application/vnd.ms-works
-.wdb: application/vnd.ms-works
-.web: application/vnd.xara
-.wiz: application/msword
-.wk1: application/x-123
-.wks: application/vnd.ms-works
-.wmf: application/x-msmetafile
-.wmf: windows/metafile
-.wml: text/vnd.wap.wml
-.wmlc: application/vnd.wap.wmlc
-.wmls: text/vnd.wap.wmlscript
-.wmlsc: application/vnd.wap.wmlscriptc
-.wmv: video/x-ms-wmv
-.word: application/msword
-.wp5: application/wordperfect
-.wp6: application/wordperfect
-.wp: application/wordperfect
-.wpd: application/wordperfect
-.wps: application/vnd.ms-works
-.wq1: application/x-lotus
-.wri: application/mswrite
-.wrl: application/x-world
-.wsc: text/scriplet
-.wsrc: application/x-wais-source
-.wtk: application/x-wintalk
-.x-png: image/png
-.xaf: x-world/x-vrml
-.xbm: image/xbm
-.xdr: video/x-amt-demorun
-.xgz: xgl/drawing
-.xhtml: application/xhtml+xml
-.xif: image/vnd.xiff
-.xl: application/excel
-.xla: application/excel
-.xlb: application/excel
-.xlc: application/excel
-.xld: application/excel
-.xlk: application/excel
-.xll: application/excel
-.xlm: application/excel
-.xls: application/excel
-.xlt: application/excel
-.xlv: application/excel
-.xlw: application/excel
-.xm: audio/xm
-.xml: text/xml
-.xmz: xgl/movie
-.xof: x-world/x-vrml
-.xpi: application/x-xpinstall
-.xpix: application/x-vnd.ls-xpix
-.xpm: image/x-xpixmap
-.xpm: image/xpm
-.xsl: application/xslt+xml
-.xsr: video/x-amt-showrun
-.xwd: image/x-xwd
-.xwd: image/x-xwindowdump
-.xyz: chemical/x-pdb
-.z: application/x-compressed
-.zip: application/zip
-.zoo: application/octet-stream
-.zsh: text/x-script.zsh
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/rails.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/rails.rb
deleted file mode 100644 (file)
index 912986d..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'mongrel'
-require 'cgi'
-
-
-module Mongrel
-  module Rails
-    # Implements a handler that can run Rails and serve files out of the
-    # Rails application's public directory.  This lets you run your Rails
-    # application with Mongrel during development and testing, then use it
-    # also in production behind a server that's better at serving the 
-    # static files.
-    #
-    # The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
-    # mapping that it should add to the list of valid mime types.
-    #
-    # It also supports page caching directly and will try to resolve a request
-    # in the following order:
-    #
-    # * If the requested exact PATH_INFO exists as a file then serve it.
-    # * If it exists at PATH_INFO+".html" exists then serve that.
-    # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
-    #
-    # This means that if you are using page caching it will actually work with Mongrel
-    # and you should see a decent speed boost (but not as fast as if you use a static
-    # server like Apache or Litespeed).
-    class RailsHandler < Mongrel::HttpHandler
-      attr_reader :files
-      attr_reader :guard
-      @@file_only_methods = ["GET","HEAD"]
-
-      def initialize(dir, mime_map = {})
-        @files = Mongrel::DirHandler.new(dir,false)
-        @guard = Mutex.new
-
-        # Register the requested MIME types
-        mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
-      end
-
-      # Attempts to resolve the request as follows:
-      #
-      # * If the requested exact PATH_INFO exists as a file then serve it.
-      # * If it exists at PATH_INFO+".html" exists then serve that.
-      # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
-      def process(request, response)
-        return if response.socket.closed?
-        
-        path_info = request.params[Mongrel::Const::PATH_INFO]
-        rest_operator = request.params[Mongrel::Const::REQUEST_URI][/^#{Regexp.escape path_info}(;[^\?]+)/, 1].to_s
-        path_info.chomp!("/")
-        
-        page_cached = path_info + rest_operator + ActionController::Base.page_cache_extension
-        get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
-
-        if get_or_head and @files.can_serve(path_info)
-          # File exists as-is so serve it up
-          @files.process(request,response)
-        elsif get_or_head and @files.can_serve(page_cached)
-          # Possible cached page, serve it up
-          request.params[Mongrel::Const::PATH_INFO] = page_cached
-          @files.process(request,response)
-        else
-          begin
-            cgi = Mongrel::CGIWrapper.new(request, response)
-            cgi.handler = self
-            # We don't want the output to be really final until we're out of the lock
-            cgi.default_really_final = false
-
-            @guard.synchronize {
-              @active_request_path = request.params[Mongrel::Const::PATH_INFO] 
-              Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
-              @active_request_path = nil
-            }
-
-            # This finalizes the output using the proper HttpResponse way
-            cgi.out("text/html",true) {""}
-          rescue Errno::EPIPE
-            response.socket.close
-          rescue Object => rails_error
-            STDERR.puts "#{Time.now}: Error calling Dispatcher.dispatch #{rails_error.inspect}"
-            STDERR.puts rails_error.backtrace.join("\n")
-          end
-        end
-      end
-
-      # Does the internal reload for Rails.  It might work for most cases, but
-      # sometimes you get exceptions.  In that case just do a real restart.
-      def reload!
-        begin
-          @guard.synchronize {
-            $".replace $orig_dollar_quote
-            GC.start
-            Dispatcher.reset_application!
-            ActionController::Routing::Routes.reload
-          }
-        end
-      end
-    end
-
-    # Creates Rails specific configuration options for people to use 
-    # instead of the base Configurator.
-    class RailsConfigurator < Mongrel::Configurator
-
-      # Creates a single rails handler and returns it so you
-      # can add it to a URI. You can actually attach it to 
-      # as many URIs as you want, but this returns the 
-      # same RailsHandler for each call.
-      #
-      # Requires the following options:
-      #
-      # * :docroot => The public dir to serve from.
-      # * :environment => Rails environment to use.
-      # * :cwd => The change to working directory
-      #
-      # And understands the following optional settings:
-      #
-      # * :mime => A map of mime types.
-      #
-      # Because of how Rails is designed you can only have
-      # one installed per Ruby interpreter (talk to them 
-      # about thread safety).  Because of this the first
-      # time you call this function it does all the config
-      # needed to get your Rails working.  After that
-      # it returns the one handler you've configured.
-      # This lets you attach Rails to any URI(s) you want,
-      # but it still protects you from threads destroying
-      # your handler.
-      def rails(options={})
-
-        return @rails_handler if @rails_handler
-
-        ops = resolve_defaults(options)
-
-        # fix up some defaults
-        ops[:environment] ||= "development"
-        ops[:docroot] ||= "public"
-        ops[:mime] ||= {}
-
-        $orig_dollar_quote = $".clone
-        ENV['RAILS_ENV'] = ops[:environment]
-        env_location = "#{ops[:cwd]}/config/environment"
-        require env_location
-        require 'dispatcher'
-        require 'mongrel/rails'
-
-        ActionController::Base.relative_url_root = ops[:prefix] if ops[:prefix]
-
-        @rails_handler = RailsHandler.new(ops[:docroot], ops[:mime])
-      end
-
-      # Reloads Rails.  This isn't too reliable really, but it
-      # should work for most minimal reload purposes.  The only reliable
-      # way to reload properly is to stop and then start the process.
-      def reload!
-        if not @rails_handler
-          raise "Rails was not configured.  Read the docs for RailsConfigurator."
-        end
-
-        log "Reloading Rails..."
-        @rails_handler.reload!
-        log "Done reloading Rails."
-
-      end
-
-      # Takes the exact same configuration as Mongrel::Configurator (and actually calls that)
-      # but sets up the additional HUP handler to call reload!.
-      def setup_rails_signals(options={})
-        ops = resolve_defaults(options)
-        setup_signals(options)
-
-        if RUBY_PLATFORM !~ /mswin/
-          # rails reload
-          trap("HUP") { log "HUP signal received."; reload!          }
-
-          log "Rails signals registered.  HUP => reload (without restart).  It might not work well."
-        end
-      end
-    end
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/stats.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/stats.rb
deleted file mode 100644 (file)
index f6cf5ab..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-# A very simple little class for doing some basic fast statistics sampling.
-# You feed it either samples of numeric data you want measured or you call
-# Stats.tick to get it to add a time delta between the last time you called it.
-# When you're done either call sum, sumsq, n, min, max, mean or sd to get 
-# the information.  The other option is to just call dump and see everything.
-#
-# It does all of this very fast and doesn't take up any memory since the samples
-# are not stored but instead all the values are calculated on the fly.
-module Mongrel
-  class Stats
-    attr_reader :sum, :sumsq, :n, :min, :max
-
-    def initialize(name)
-      @name = name
-      reset
-    end
-
-    # Resets the internal counters so you can start sampling again.
-    def reset
-      @sum = 0.0
-      @sumsq = 0.0
-      @last_time = Time.new
-      @n = 0.0
-      @min = 0.0
-      @max = 0.0
-    end
-
-    # Adds a sampling to the calculations.
-    def sample(s)
-      @sum += s
-      @sumsq += s * s
-      if @n == 0
-        @min = @max = s
-      else
-        @min = s if @min > s
-        @max = s if @max < s
-      end
-      @n+=1
-    end
-
-    # Dump this Stats object with an optional additional message.
-    def dump(msg = "", out=STDERR)
-      out.puts "#{msg}: #{self.to_s}"
-    end
-
-    # Returns a common display (used by dump)
-    def to_s  
-    "[#{@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]
-    end
-
-
-    # Calculates and returns the mean for the data passed so far.
-    def mean
-      @sum / @n
-    end
-
-    # Calculates the standard deviation of the data so far.
-    def sd
-      # (sqrt( ((s).sumsq - ( (s).sum * (s).sum / (s).n)) / ((s).n-1) ))
-      begin
-        return Math.sqrt( (@sumsq - ( @sum * @sum / @n)) / (@n-1) )
-      rescue Errno::EDOM
-        return 0.0
-      end
-    end
-
-
-    # Adds a time delta between now and the last time you called this.  This
-    # will give you the average time between two activities.
-    # 
-    # An example is:
-    #
-    #  t = Stats.new("do_stuff")
-    #  10000.times { do_stuff(); t.tick }
-    #  t.dump("time")
-    #
-    def tick
-      now = Time.now
-      sample(now - @last_time)
-      @last_time = now
-    end
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/tcphack.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/tcphack.rb
deleted file mode 100644 (file)
index 634f9dd..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-
-# A modification proposed by Sean Treadway that increases the default accept
-# queue of TCPServer to 1024 so that it handles more concurrent requests.
-class TCPServer
-   def initialize_with_backlog(*args)
-     initialize_without_backlog(*args)
-     listen(1024)
-   end
-
-   alias_method :initialize_without_backlog, :initialize
-   alias_method :initialize, :initialize_with_backlog
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/uri_classifier.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/lib/mongrel/uri_classifier.rb
deleted file mode 100644 (file)
index f39ccc9..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-
-module Mongrel
-  class URIClassifier
-  
-    class RegistrationError < RuntimeError
-    end
-    class UsageError < RuntimeError
-    end
-
-    attr_reader :handler_map   
-    # Returns the URIs that have been registered with this classifier so far.
-    def uris
-      @handler_map.keys
-    end
-
-    def initialize
-      @handler_map = {}
-      @matcher = //
-      @root_handler = nil
-    end
-    
-    # Register a handler object at a particular URI. The handler can be whatever 
-    # you want, including an array. It's up to you what to do with it.
-    #
-    # Registering a handler is not necessarily threadsafe, so be careful if you go
-    # mucking around once the server is running.
-    def register(uri, handler)
-      raise RegistrationError, "#{uri.inspect} is already registered" if @handler_map[uri]
-      raise RegistrationError, "URI is empty" if !uri or uri.empty?
-      raise RegistrationError, "URI must begin with a \"#{Const::SLASH}\"" unless uri[0..0] == Const::SLASH
-      @handler_map[uri.dup] = handler
-      rebuild
-    end
-    
-    # Unregister a particular URI and its handler.
-    def unregister(uri)
-      handler = @handler_map.delete(uri)
-      raise RegistrationError, "#{uri.inspect} was not registered" unless handler
-      rebuild
-      handler
-    end
-    
-    # Resolve a request URI by finding the best partial match in the registered 
-    # handler URIs.
-    def resolve(request_uri)
-      if @root_handler
-        # Optimization for the pathological case of only one handler on "/"; e.g. Rails
-        [Const::SLASH, request_uri, @root_handler]
-      elsif match = @matcher.match(request_uri)
-        uri = match.to_s
-        # A root mounted ("/") handler must resolve such that path info matches the original URI.
-        [uri, (uri == Const::SLASH ? request_uri : match.post_match), @handler_map[uri]]
-      else
-        [nil, nil, nil]
-      end
-    end
-        
-    private
-    
-    def rebuild
-      if @handler_map.size == 1 and @handler_map[Const::SLASH]
-        @root_handler = @handler_map.values.first
-      else
-        @root_handler = nil
-        routes = @handler_map.keys.sort.sort_by do |uri|
-          -uri.length
-        end
-        @matcher = Regexp.new(routes.map do |uri|
-          Regexp.new('^' + Regexp.escape(uri))
-        end.join('|'))
-      end
-    end    
-    
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel-public_cert.pem b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel-public_cert.pem
deleted file mode 100644 (file)
index 4fe190d..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDUDCCAjigAwIBAgIBADANBgkqhkiG9w0BAQUFADBOMRwwGgYDVQQDDBNtb25n
-cmVsLWRldmVsb3BtZW50MRkwFwYKCZImiZPyLGQBGRYJcnVieWZvcmdlMRMwEQYK
-CZImiZPyLGQBGRYDb3JnMB4XDTA3MDkxNjEwMzI0OVoXDTA4MDkxNTEwMzI0OVow
-TjEcMBoGA1UEAwwTbW9uZ3JlbC1kZXZlbG9wbWVudDEZMBcGCgmSJomT8ixkARkW
-CXJ1Ynlmb3JnZTETMBEGCgmSJomT8ixkARkWA29yZzCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAMb9v3B01eOHk3FyypbQgKXzJplUE5P6dXoG+xpPm0Lv
-P7BQmeMncOwqQ7zXpVQU+lTpXtQFTsOE3vL7KnhQFJKGvUAkbh24VFyopu1I0yqF
-mGu4nRqNXGXVj8TvLSj4S1WpSRLAa0acLPNyKhGmoV9+crqQypSjM6XKjBeppifo
-4eBmWGjiJEYMIJBvJZPJ4rAVDDA8C6CM1m3gMBGNh8ELDhU8HI9AP3dMIkTI2Wx9
-9xkJwHdroAaS0IFFtYChrwee4FbCF1FHDgoTosMwa47DrLHg4hZ6ojaKwK5QVWEV
-XGb6ju5UqpktnSWF2W+Lvl/K0tI42OH2CAhebT1gEVUCAwEAAaM5MDcwCQYDVR0T
-BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFGHChyMSZ16u9WOzKhgJSQ9lqDc5
-MA0GCSqGSIb3DQEBBQUAA4IBAQA/lfeN2WdB1xN+82tT7vNS4HOjRQw6MUh5yktu
-GQjaGqm0UB+aX0Z9y0B0qpfv9rj7nmIvEGiwBmDepNWYCGuW15JyqpN7QVVnG2xS
-Mrame7VqgjM7A+VGDD5In5LtWbM/CHAATvvFlQ5Ph13YE1EdnVbZ65c+KQv+5sFY
-Q+zEop74d878uaC/SAHHXS46TiXneocaLSYw1CEZs/MAIy+9c4Q5ESbGpgnfg1Ad
-6lwl7k3hsNHO/+tZzx4HJtOXDI1yAl3+q6T9J0yI3z97EinwvAKhS1eyOI2Y5eeT
-tbQaNYkU127B3l/VNpd8fQm3Jkl/PqCCmDBQjUszFrJEODug
------END CERTIFICATE-----
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel.gemspec b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/mongrel.gemspec
deleted file mode 100644 (file)
index 105da7d..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-\r
-# Gem::Specification for Mongrel-1.1.5\r
-# Originally generated by Echoe\r
-\r
-Gem::Specification.new do |s|\r
-  s.name = %q{mongrel}\r
-  s.version = "1.1.5"\r
-  s.platform = %q{x86-mswin32-60}\r
-\r
-  s.specification_version = 2 if s.respond_to? :specification_version=\r
-\r
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=\r
-  s.authors = ["Zed A. Shaw"]\r
-  s.date = %q{2008-05-22}\r
-  s.default_executable = %q{mongrel_rails}\r
-  s.description = %q{A small fast HTTP library and server that runs Rails, Camping, Nitro and Iowa apps.}\r
-  s.email = %q{}\r
-  s.executables = ["mongrel_rails"]\r
-  s.has_rdoc = true\r
-  s.homepage = %q{http://mongrel.rubyforge.org}\r
-  s.require_paths = ["lib", "ext"]\r
-  s.required_ruby_version = Gem::Requirement.new(">= 1.8.4")\r
-  s.rubyforge_project = %q{mongrel}\r
-  s.rubygems_version = %q{1.0.1}\r
-  s.summary = %q{A small fast HTTP library and server that runs Rails, Camping, Nitro and Iowa apps.}\r
-  s.test_files = ["test/test_cgi_wrapper.rb", "test/test_command.rb", "test/test_conditional.rb", "test/test_configurator.rb", "test/test_debug.rb", "test/test_handlers.rb", "test/test_http11.rb", "test/test_redirect_handler.rb", "test/test_request_progress.rb", "test/test_response.rb", "test/test_stats.rb", "test/test_uriclassifier.rb", "test/test_ws.rb"]\r
-\r
-  s.add_dependency(%q<gem_plugin>, [">= 0.2.3"])\r
-  s.add_dependency(%q<cgi_multipart_eof_fix>, [">= 2.4"])\r
-end\r
-\r
-\r
-# # Original Rakefile source (requires the Echoe gem):\r
-# \r
-# \r
-# require 'rubygems'\r
-# gem 'echoe', '>=2.7.5'\r
-# require 'echoe'\r
-# \r
-# e = Echoe.new("mongrel") do |p|\r
-#   p.summary = "A small fast HTTP library and server that runs Rails, Camping, Nitro and Iowa apps."\r
-#   p.author ="Zed A. Shaw"\r
-#   p.clean_pattern = ['ext/http11/*.{bundle,so,o,obj,pdb,lib,def,exp}', 'lib/*.{bundle,so,o,obj,pdb,lib,def,exp}', 'ext/http11/Makefile', 'pkg', 'lib/*.bundle', '*.gem', 'site/output', '.config', 'lib/http11.jar', 'ext/http11_java/classes', 'coverage', 'doc']\r
-#   p.url = "http://mongrel.rubyforge.org"\r
-#   p.rdoc_pattern = ['README', 'LICENSE', 'CHANGELOG', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc']\r
-#   p.docs_host = 'mongrel.cloudbur.st:/home/eweaver/www/mongrel/htdocs/web'\r
-#   p.ignore_pattern = /^(pkg|site|projects|doc|log)|CVS|\.log/\r
-#   p.ruby_version = '>=1.8.4'\r
-#   p.dependencies = ['gem_plugin >=0.2.3']  \r
-#   p.extension_pattern = nil\r
-#   \r
-#     when 'eweaver' \r
-#     when 'luislavena'\r
-#   end\r
-#   \r
-#   p.need_tar_gz = false\r
-#   p.need_tgz = true\r
-# \r
-#   if RUBY_PLATFORM !~ /mswin|java/\r
-#     p.extension_pattern = ["ext/**/extconf.rb"]\r
-#   end\r
-# \r
-#   p.eval = proc do\r
-#     case RUBY_PLATFORM\r
-#     when /mswin/\r
-#       self.files += ['lib/http11.so']\r
-#       self.platform = Gem::Platform::CURRENT\r
-#       add_dependency('cgi_multipart_eof_fix', '>= 2.4')\r
-#     when /java/\r
-#       self.files += ['lib/http11.jar']\r
-#       self.platform = 'jruby' # XXX Is this right?\r
-#     else\r
-#       add_dependency('daemons', '>= 1.0.3')\r
-#       add_dependency('fastthread', '>= 1.0.1')\r
-#       add_dependency('cgi_multipart_eof_fix', '>= 2.4')\r
-#     end\r
-#   end\r
-# \r
-# end\r
-# \r
-# #### Ragel builder\r
-# \r
-# desc "Rebuild the Ragel sources"\r
-# task :ragel do\r
-#   Dir.chdir "ext/http11" do\r
-#     target = "http11_parser.c"\r
-#     File.unlink target if File.exist? target\r
-#     sh "ragel http11_parser.rl | rlgen-cd -G2 -o #{target}"\r
-#     raise "Failed to build C source" unless File.exist? target\r
-#   end\r
-#   Dir.chdir "ext/http11" do\r
-#     target = "../../ext/http11_java/org/jruby/mongrel/Http11Parser.java"\r
-#     File.unlink target if File.exist? target\r
-#     sh "ragel -J http11_parser.java.rl | rlgen-java -o #{target}"\r
-#     raise "Failed to build Java source" unless File.exist? target\r
-#   end\r
-# end\r
-# \r
-# #### Pre-compiled extensions for alternative platforms\r
-# \r
-# def move_extensions\r
-#   Dir["ext/**/*.#{Config::CONFIG['DLEXT']}"].each { |file| mv file, "lib/" }\r
-# end\r
-# \r
-# def java_classpath_arg\r
-#   # A myriad of ways to discover the JRuby classpath\r
-#   classpath = begin\r
-#     require 'java'\r
-#     # Already running in a JRuby JVM\r
-#     Java::java.lang.System.getProperty('java.class.path')\r
-#   rescue LoadError\r
-#     ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] && FileList["#{ENV['JRUBY_HOME']}/lib/*.jar"].join(File::PATH_SEPARATOR)\r
-#   end\r
-#   classpath ? "-cp #{classpath}" : ""\r
-# end\r
-# \r
-# case RUBY_PLATFORM\r
-# when /mswin/\r
-#   filename = "lib/http11.so"\r
-#   file filename do\r
-#     Dir.chdir("ext/http11") do\r
-#       ruby "extconf.rb"\r
-#       system(PLATFORM =~ /mswin/ ? 'nmake' : 'make')\r
-#     end\r
-#     move_extensions\r
-#   end\r
-#   task :compile => [filename]\r
-# \r
-# when /java/\r
-# \r
-#   # Avoid JRuby in-process launching problem\r
-#   begin\r
-#     require 'jruby'\r
-#     JRuby.runtime.instance_config.run_ruby_in_process = false \r
-#   rescue LoadError\r
-#   end\r
-# \r
-#   filename = "lib/http11.jar"\r
-#   file filename do\r
-#     build_dir = "ext/http11_java/classes"\r
-#     mkdir_p build_dir\r
-#     sources = FileList['ext/http11_java/**/*.java'].join(' ')\r
-#     sh "javac -target 1.4 -source 1.4 -d #{build_dir} #{java_classpath_arg} #{sources}"\r
-#     sh "jar cf lib/http11.jar -C #{build_dir} ."\r
-#     move_extensions\r
-#   end\r
-#   task :compile => [filename]\r
-# \r
-# end\r
-# \r
-# #### Project-wide install and uninstall tasks\r
-# \r
-# def sub_project(project, *targets)\r
-#   targets.each do |target|\r
-#     Dir.chdir "projects/#{project}" do\r
-#       unless RUBY_PLATFORM =~ /mswin/\r
-#         sh("rake #{target.to_s}") # --trace \r
-#       end\r
-#     end\r
-#   end\r
-# end\r
-# \r
-# desc "Package Mongrel and all subprojects"\r
-# task :package_all => [:package] do\r
-#   sub_project("gem_plugin", :package)\r
-#   sub_project("cgi_multipart_eof_fix", :package)\r
-#   sub_project("fastthread", :package)\r
-#   sub_project("mongrel_status", :package)\r
-#   sub_project("mongrel_upload_progress", :package)\r
-#   sub_project("mongrel_console", :package)\r
-#   sub_project("mongrel_cluster", :package)\r
-#   sub_project("mongrel_experimental", :package)\r
-# \r
-#   sh("rake java package") unless RUBY_PLATFORM =~ /java/\r
-#   \r
-#   # XXX Broken by RubyGems 0.9.5\r
-#   # sub_project("mongrel_service", :package) if RUBY_PLATFORM =~ /mswin/\r
-#   # sh("rake mswin package") unless RUBY_PLATFORM =~ /mswin/\r
-# end\r
-# \r
-# task :install_requirements do\r
-#   # These run before Mongrel is installed\r
-#   sub_project("gem_plugin", :install)\r
-#   sub_project("cgi_multipart_eof_fix", :install)\r
-#   sub_project("fastthread", :install)\r
-# end\r
-# \r
-# desc "for Mongrel and all subprojects"\r
-# task :install => [:install_requirements] do\r
-#   # These run after Mongrel is installed\r
-#   sub_project("mongrel_status", :install)\r
-#   sub_project("mongrel_upload_progress", :install)\r
-#   sub_project("mongrel_console", :install)\r
-#   sub_project("mongrel_cluster", :install)\r
-#   # sub_project("mongrel_experimental", :install)\r
-#   sub_project("mongrel_service", :install) if RUBY_PLATFORM =~ /mswin/\r
-# end\r
-# \r
-# desc "for Mongrel and all its subprojects"\r
-# task :uninstall => [:clean] do\r
-#   sub_project("mongrel_status", :uninstall)\r
-#   sub_project("cgi_multipart_eof_fix", :uninstall)\r
-#   sub_project("mongrel_upload_progress", :uninstall)\r
-#   sub_project("mongrel_console", :uninstall)\r
-#   sub_project("gem_plugin", :uninstall)\r
-#   sub_project("fastthread", :uninstall)\r
-#   # sub_project("mongrel_experimental", :uninstall)\r
-#   sub_project("mongrel_service", :uninstall) if RUBY_PLATFORM =~ /mswin/\r
-# end\r
-# \r
-# desc "for Mongrel and all its subprojects"\r
-# task :clean do\r
-#   sub_project("gem_plugin", :clean)\r
-#   sub_project("cgi_multipart_eof_fix", :clean)\r
-#   sub_project("fastthread", :clean)\r
-#   sub_project("mongrel_status", :clean)\r
-#   sub_project("mongrel_upload_progress", :clean)\r
-#   sub_project("mongrel_console", :clean)\r
-#   sub_project("mongrel_cluster", :clean)\r
-#   sub_project("mongrel_experimental", :clean)\r
-#   sub_project("mongrel_service", :clean) if RUBY_PLATFORM =~ /mswin/\r
-# end\r
-# \r
-# #### Site upload tasks\r
-# \r
-# namespace :site do\r
-#   desc "Upload the coverage report"\r
-#   task :coverage => [:rcov] do\r
-#     sh "rsync -azv --no-perms --no-times test/coverage/* mongrel.cloudbur.st:/home/eweaver/www/mongrel/htdocs/web/coverage" rescue nil\r
-#   end\r
-# end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mime.yaml b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mime.yaml
deleted file mode 100644 (file)
index 6e7bb04..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
---- 
-.jpeg: image/jpeg
-.png: image/test
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mongrel.conf b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/mongrel.conf
deleted file mode 100644 (file)
index 3daa23a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-uri "/fromconf", :handler => Mongrel::Error404Handler.new("test")
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_cgi_wrapper.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_cgi_wrapper.rb
deleted file mode 100644 (file)
index 449f6d0..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-
-require 'test/testhelp'
-
-class MockHttpRequest
-  attr_reader :body
-
-  def params
-    return { 'REQUEST_METHOD' => 'GET'}
-  end
-end
-
-class CGIWrapperTest < Test::Unit::TestCase
-  
-  def test_set_cookies_output_cookies
-    request = MockHttpRequest.new
-    response = nil # not needed for this test
-    output_headers = {}
-    
-    cgi = Mongrel::CGIWrapper.new(request, response) 
-    session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::MemoryStore)
-    cgi.send_cookies(output_headers)
-    
-    assert(output_headers.has_key?("Set-Cookie"))
-    assert_equal("_session_id="+session.session_id+"; path=", output_headers["Set-Cookie"])
-  end
-end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_command.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_command.rb
deleted file mode 100644 (file)
index 3cb9643..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-class TestCommand < GemPlugin::Plugin "/commands"
-  include Mongrel::Command::Base
-
-  def configure
-    options [
-      ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
-      ['', '--user USER', "User to run as", :@user, nil],
-      ["-d", "--daemonize", "Whether to run in the background or not", :@daemon, false],
-      ["-x", "--test", "Used to let the test run failures", :@test, false],
-    ]
-  end
-
-  def validate
-    valid_dir? ".", "Can't validate current directory."
-    valid_exists? "Rakefile", "Rakefile not there, test is invalid."
-    if @test
-      valid_exist? "BADFILE", "Yeah, badfile"
-      valid_file? "BADFILE", "Not even a file"
-      valid_dir? "BADDIR", "No dir here"
-      valid? false, "Total failure"
-    end
-
-    return @valid
-  end
-
-
-  def run
-    $test_command_ran = true
-  end
-end
-
-class CommandTest < Test::Unit::TestCase
-
-  def setup
-    $test_command_ran = false
-  end
-
-  def teardown
-  end
-
-  def run_cmd(args)
-    Mongrel::Command::Registry.instance.run args
-  end
-
-  def test_run_command
-    redirect_test_io do
-      run_cmd ["testcommand"]
-      assert $test_command_ran, "command didn't run"
-    end
-  end
-
-  def test_command_error
-    redirect_test_io do
-      run_cmd ["crapcommand"]
-    end
-  end
-
-  def test_command_listing
-    redirect_test_io do
-      run_cmd ["help"]
-    end
-  end
-
-  def test_options
-    redirect_test_io do
-      run_cmd ["testcommand","-h"]
-      run_cmd ["testcommand","--help"]
-      run_cmd ["testcommand","-e","test","-d","--user"]
-    end
-  end
-
-  def test_version
-    redirect_test_io do
-      run_cmd ["testcommand", "--version"]
-    end
-  end
-
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_conditional.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_conditional.rb
deleted file mode 100644 (file)
index cd3ce06..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-include Mongrel
-
-class ConditionalResponseTest < Test::Unit::TestCase
-  def setup
-    @server = HttpServer.new('127.0.0.1', 3501)
-    @server.register('/', Mongrel::DirHandler.new('.'))
-    @server.run
-    
-    @http = Net::HTTP.new(@server.host, @server.port)
-
-    # get the ETag and Last-Modified headers
-    @path = '/README'
-    res = @http.start { |http| http.get(@path) }
-    assert_not_nil @etag = res['ETag']
-    assert_not_nil @last_modified = res['Last-Modified']
-    assert_not_nil @content_length = res['Content-Length']
-  end
-
-  def teardown
-    @server.stop(true)
-  end
-
-  # status should be 304 Not Modified when If-None-Match is the matching ETag
-  def test_not_modified_via_if_none_match
-    assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag
-  end
-
-  # status should be 304 Not Modified when If-Modified-Since is the matching Last-Modified date
-  def test_not_modified_via_if_modified_since
-    assert_status_for_get_and_head Net::HTTPNotModified, 'If-Modified-Since' => @last_modified
-  end
-
-  # status should be 304 Not Modified when If-None-Match is the matching ETag
-  # and If-Modified-Since is the matching Last-Modified date
-  def test_not_modified_via_if_none_match_and_if_modified_since
-    assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => @last_modified
-  end
-
-  # status should be 200 OK when If-None-Match is invalid
-  def test_invalid_if_none_match
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid'
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid', 'If-Modified-Since' => @last_modified
-  end
-
-  # status should be 200 OK when If-Modified-Since is invalid
-  def test_invalid_if_modified_since
-    assert_status_for_get_and_head Net::HTTPOK,                           'If-Modified-Since' => 'invalid'
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => 'invalid'
-  end
-
-  # status should be 304 Not Modified when If-Modified-Since is greater than the Last-Modified header, but less than the system time
-  def test_if_modified_since_greater_than_last_modified
-    sleep 2
-    last_modified_plus_1 = (Time.httpdate(@last_modified) + 1).httpdate
-    assert_status_for_get_and_head Net::HTTPNotModified,                           'If-Modified-Since' => last_modified_plus_1
-    assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_plus_1
-  end
-
-  # status should be 200 OK when If-Modified-Since is less than the Last-Modified header
-  def test_if_modified_since_less_than_last_modified
-    last_modified_minus_1 = (Time.httpdate(@last_modified) - 1).httpdate
-    assert_status_for_get_and_head Net::HTTPOK,                           'If-Modified-Since' => last_modified_minus_1
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_minus_1
-  end
-
-  # status should be 200 OK when If-Modified-Since is a date in the future
-  def test_future_if_modified_since
-    the_future = Time.at(2**31-1).httpdate
-    assert_status_for_get_and_head Net::HTTPOK,                           'If-Modified-Since' => the_future
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => the_future
-  end
-
-  # status should be 200 OK when If-None-Match is a wildcard
-  def test_wildcard_match
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*'
-    assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*', 'If-Modified-Since' => @last_modified
-  end
-
-  private
-
-    # assert the response status is correct for GET and HEAD
-    def assert_status_for_get_and_head(response_class, headers = {})
-      %w{ get head }.each do |method|
-        res = @http.send(method, @path, headers)
-        assert_kind_of response_class, res
-        assert_equal @etag, res['ETag']
-        case response_class.to_s
-          when 'Net::HTTPNotModified' then
-            assert_nil res['Last-Modified']
-            assert_nil res['Content-Length']
-          when 'Net::HTTPOK' then
-            assert_equal @last_modified, res['Last-Modified']
-            assert_equal @content_length, res['Content-Length']
-          else
-            fail "Incorrect response class: #{response_class}"
-        end
-      end
-    end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_configurator.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_configurator.rb
deleted file mode 100644 (file)
index dd99f00..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-$test_plugin_fired = 0
-
-class TestPlugin < GemPlugin::Plugin "/handlers"
-  include Mongrel::HttpHandlerPlugin
-
-  def process(request, response)
-    $test_plugin_fired += 1
-  end
-end
-
-
-class Sentinel < GemPlugin::Plugin "/handlers"
-  include Mongrel::HttpHandlerPlugin
-
-  def process(request, response)
-    raise "This Sentinel plugin shouldn't run."
-  end
-end
-
-
-class ConfiguratorTest < Test::Unit::TestCase
-
-  def test_base_handler_config
-    @config = nil
-
-    redirect_test_io do
-      @config = Mongrel::Configurator.new :host => "localhost" do
-        listener :port => 4501 do
-          # 2 in front should run, but the sentinel shouldn't since dirhandler processes the request
-          uri "/", :handler => plugin("/handlers/testplugin")
-          uri "/", :handler => plugin("/handlers/testplugin")
-          uri "/", :handler => Mongrel::DirHandler.new(".")
-          uri "/", :handler => plugin("/handlers/testplugin")
-
-          uri "/test", :handler => plugin("/handlers/testplugin")
-          uri "/test", :handler => plugin("/handlers/testplugin")
-          uri "/test", :handler => Mongrel::DirHandler.new(".")
-          uri "/test", :handler => plugin("/handlers/testplugin")
-
-          debug "/"
-          setup_signals
-
-          run_config(File.dirname(__FILE__) + "/../test/mongrel.conf")
-          load_mime_map(File.dirname(__FILE__) + "/../test/mime.yaml")
-
-          run
-        end
-      end
-    end
-
-    # pp @config.listeners.values.first.classifier.routes
-
-    @config.listeners.each do |host,listener| 
-      assert listener.classifier.uris.length == 3, "Wrong number of registered URIs"
-      assert listener.classifier.uris.include?("/"),  "/ not registered"
-      assert listener.classifier.uris.include?("/test"), "/test not registered"
-    end
-
-    res = Net::HTTP.get(URI.parse('http://localhost:4501/test'))
-    assert res != nil, "Didn't get a response"
-    assert $test_plugin_fired == 3, "Test filter plugin didn't run 3 times."
-
-    redirect_test_io do
-      res = Net::HTTP.get(URI.parse('http://localhost:4501/'))
-
-      assert res != nil, "Didn't get a response"
-      assert $test_plugin_fired == 6, "Test filter plugin didn't run 6 times."
-    end
-
-    redirect_test_io do
-      @config.stop(false, true)
-    end
-
-    assert_raise Errno::EBADF, Errno::ECONNREFUSED do
-      res = Net::HTTP.get(URI.parse("http://localhost:4501/"))
-    end
-  end
-
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_debug.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_debug.rb
deleted file mode 100644 (file)
index 2f7be24..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-require 'mongrel/debug'
-
-class MongrelDbgTest < Test::Unit::TestCase
-
-  def test_tracing_to_log
-    FileUtils.rm_rf "log/mongrel_debug"
-
-    MongrelDbg::configure
-    out = StringIO.new
-
-    MongrelDbg::begin_trace(:rails)
-    MongrelDbg::trace(:rails, "Good stuff")
-    MongrelDbg::end_trace(:rails)
-
-    assert File.exist?("log/mongrel_debug"), "Didn't make logging directory"
-  end
-
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_handlers.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_handlers.rb
deleted file mode 100644 (file)
index 1005dd0..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-class SimpleHandler < Mongrel::HttpHandler
-  def process(request, response)
-    response.start do |head,out|
-      head["Content-Type"] = "text/html"
-      results = "<html><body>Your request:<br /><pre>#{request.params.to_yaml}</pre><a href=\"/files\">View the files.</a></body></html>"
-      out << results
-    end
-  end
-end
-
-class DumbHandler < Mongrel::HttpHandler
-  def process(request, response)
-    response.start do |head,out|
-      head["Content-Type"] = "text/html"
-      out.write("test")
-    end
-  end
-end
-
-def check_status(results, expecting)
-  results.each do |res|
-    assert(res.kind_of?(expecting), "Didn't get #{expecting}, got: #{res.class}")
-  end
-end
-
-class HandlersTest < Test::Unit::TestCase
-
-  def setup
-    stats = Mongrel::StatisticsFilter.new(:sample_rate => 1)
-
-    @config = Mongrel::Configurator.new :host => '127.0.0.1', :port => 9998 do
-      listener do
-        uri "/", :handler => SimpleHandler.new
-        uri "/", :handler => stats
-        uri "/404", :handler => Mongrel::Error404Handler.new("Not found")
-        uri "/dumb", :handler => Mongrel::DeflateFilter.new
-        uri "/dumb", :handler => DumbHandler.new, :in_front => true
-        uri "/files", :handler => Mongrel::DirHandler.new("doc")
-        uri "/files_nodir", :handler => Mongrel::DirHandler.new("doc", listing_allowed=false, index_html="none")
-        uri "/status", :handler => Mongrel::StatusHandler.new(:stats_filter => stats)
-        uri "/relative", :handler => Mongrel::DirHandler.new(nil, listing_allowed=false, index_html="none")
-      end
-    end
-    
-    File.open("/tmp/testfile", 'w') do
-      # Do nothing
-    end
-    
-    @config.run
-  end
-
-  def teardown
-    @config.stop(false, true)
-    File.delete "/tmp/testfile"
-  end
-
-  def test_more_web_server
-    res = hit([ "http://localhost:9998/test",
-          "http://localhost:9998/dumb",
-          "http://localhost:9998/404",
-          "http://localhost:9998/files/rdoc/index.html",
-          "http://localhost:9998/files/rdoc/nothere.html",
-          "http://localhost:9998/files/rdoc/",
-          "http://localhost:9998/files_nodir/rdoc/",
-          "http://localhost:9998/status",
-    ])
-    check_status res, String
-  end
-  
-  def test_nil_dirhandler
-    # Camping uses this internally
-    handler = Mongrel::DirHandler.new(nil, false)  
-    assert handler.can_serve("/tmp/testfile")
-    # Not a bug! A nil @file parameter is the only circumstance under which
-    # we are allowed to serve any existing file
-    assert handler.can_serve("../../../../../../../../../../tmp/testfile")
-  end
-  
-  def test_non_nil_dirhandler_is_not_vulnerable_to_path_traversal
-    # The famous security bug of Mongrel 1.1.2
-    handler = Mongrel::DirHandler.new("/doc", false)
-    assert_nil handler.can_serve("/tmp/testfile")
-    assert_nil handler.can_serve("../../../../../../../../../../tmp/testfile")
-  end
-
-  def test_deflate
-    Net::HTTP.start("localhost", 9998) do |h|
-      # Test that no accept-encoding returns a non-deflated response
-      req = h.get("/dumb")
-      assert(
-        !req['Content-Encoding'] ||
-        !req['Content-Encoding'].include?('deflate'))
-      assert_equal "test", req.body
-
-      req = h.get("/dumb", {"Accept-Encoding" => "deflate"})
-      # -MAX_WBITS stops zlib from looking for a zlib header
-      inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
-      assert req['Content-Encoding'].include?('deflate')
-      assert_equal "test", inflater.inflate(req.body)
-    end
-  end
-
-  # TODO: find out why this fails on win32 but nowhere else
-  #def test_posting_fails_dirhandler
-  #  req = Net::HTTP::Post.new("http://localhost:9998/files/rdoc/")
-  #  req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
-  #  res = hit [["http://localhost:9998/files/rdoc/",req]]
-  #  check_status res, Net::HTTPNotFound
-  #end
-
-  def test_unregister
-    @config.listeners["127.0.0.1:9998"].unregister("/")
-  end
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_http11.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_http11.rb
deleted file mode 100644 (file)
index da311af..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-include Mongrel
-
-class HttpParserTest < Test::Unit::TestCase
-    
-  def test_parse_simple
-    parser = HttpParser.new
-    req = {}
-    http = "GET / HTTP/1.1\r\n\r\n"
-    nread = parser.execute(req, http, 0)
-
-    assert nread == http.length, "Failed to parse the full HTTP request"
-    assert parser.finished?, "Parser didn't finish"
-    assert !parser.error?, "Parser had error"
-    assert nread == parser.nread, "Number read returned from execute does not match"
-
-    assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
-    assert_equal '/', req['REQUEST_PATH']
-    assert_equal 'HTTP/1.1', req['HTTP_VERSION']
-    assert_equal '/', req['REQUEST_URI']
-    assert_equal 'CGI/1.2', req['GATEWAY_INTERFACE']
-    assert_equal 'GET', req['REQUEST_METHOD']    
-    assert_nil req['FRAGMENT']
-    assert_nil req['QUERY_STRING']
-    
-    parser.reset
-    assert parser.nread == 0, "Number read after reset should be 0"
-  end
-  def test_parse_dumbfuck_headers
-    parser = HttpParser.new
-    req = {}
-    should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
-    nread = parser.execute(req, should_be_good, 0)
-    assert_equal should_be_good.length, nread
-    assert parser.finished?
-    assert !parser.error?
-
-    nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
-    parser = HttpParser.new
-    req = {}
-    #nread = parser.execute(req, nasty_pound_header, 0)
-    #assert_equal nasty_pound_header.length, nread
-    #assert parser.finished?
-    #assert !parser.error?
-  end
-  
-  def test_parse_error
-    parser = HttpParser.new
-    req = {}
-    bad_http = "GET / SsUTF/1.1"
-
-    error = false
-    begin
-      nread = parser.execute(req, bad_http, 0)
-    rescue => details
-      error = true
-    end
-
-    assert error, "failed to throw exception"
-    assert !parser.finished?, "Parser shouldn't be finished"
-    assert parser.error?, "Parser SHOULD have error"
-  end
-
-  def test_fragment_in_uri
-    parser = HttpParser.new
-    req = {}
-    get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
-    assert_nothing_raised do
-      parser.execute(req, get, 0)
-    end
-    assert parser.finished?
-    assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
-    assert_equal 'posts-17408', req['FRAGMENT']
-  end
-
-  # lame random garbage maker
-  def rand_data(min, max, readable=true)
-    count = min + ((rand(max)+1) *10).to_i
-    res = count.to_s + "/"
-    
-    if readable
-      res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
-    else
-      res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
-    end
-
-    return res
-  end
-  
-
-  def test_horrible_queries
-    parser = HttpParser.new
-
-    # then that large header names are caught
-    10.times do |c|
-      get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
-      assert_raises Mongrel::HttpParserError do
-        parser.execute({}, get, 0)
-        parser.reset
-      end
-    end
-
-    # then that large mangled field values are caught
-    10.times do |c|
-      get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
-      assert_raises Mongrel::HttpParserError do
-        parser.execute({}, get, 0)
-        parser.reset
-      end
-    end
-
-    # then large headers are rejected too
-    get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
-    get << "X-Test: test\r\n" * (80 * 1024)
-    assert_raises Mongrel::HttpParserError do
-      parser.execute({}, get, 0)
-      parser.reset
-    end
-
-    # finally just that random garbage gets blocked all the time
-    10.times do |c|
-      get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
-      assert_raises Mongrel::HttpParserError do
-        parser.execute({}, get, 0)
-        parser.reset
-      end
-    end
-
-  end
-
-
-
-  def test_query_parse
-    res = HttpRequest.query_parse("zed=1&frank=#{HttpRequest.escape('&&& ')}")
-    assert res["zed"], "didn't get the request right"
-    assert res["frank"], "no frank"
-    assert_equal "1", res["zed"], "wrong result"
-    assert_equal "&&& ", HttpRequest.unescape(res["frank"]), "wrong result"
-
-    res = HttpRequest.query_parse("zed=1&zed=2&zed=3&frank=11;zed=45")
-    assert res["zed"], "didn't get the request right"
-    assert res["frank"], "no frank"
-    assert_equal 4,res["zed"].length, "wrong number for zed"
-    assert_equal "11",res["frank"], "wrong number for frank"
-  end
-  
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_redirect_handler.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_redirect_handler.rb
deleted file mode 100644 (file)
index 2e03d48..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-class RedirectHandlerTest < Test::Unit::TestCase
-
-  def setup
-    redirect_test_io do
-      @server = Mongrel::HttpServer.new('127.0.0.1', 9998)
-    end
-    @server.run
-    @client = Net::HTTP.new('127.0.0.1', 9998)
-  end
-
-  def teardown
-    @server.stop(true)
-  end
-
-  def test_simple_redirect
-    tester = Mongrel::RedirectHandler.new('/yo')
-    @server.register("/test", tester)
-
-    sleep(1)
-    res = @client.request_get('/test')
-    assert res != nil, "Didn't get a response"
-    assert_equal ['/yo'], res.get_fields('Location')
-  end
-
-  def test_rewrite
-    tester = Mongrel::RedirectHandler.new(/(\w+)/, '+\1+')
-    @server.register("/test", tester)
-
-    sleep(1)
-    res = @client.request_get('/test/something')
-    assert_equal ['/+test+/+something+'], res.get_fields('Location')
-  end
-
-end
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_request_progress.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_request_progress.rb
deleted file mode 100644 (file)
index ba21c27..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-class UploadBeginHandler < Mongrel::HttpHandler
-  attr_reader :request_began, :request_progressed, :request_processed
-
-  def initialize
-    @request_notify = true
-  end
-
-  def reset
-    @request_began = false
-    @request_progressed = false
-    @request_processed = false
-  end
-
-  def request_begins(params)
-    @request_began = true
-  end
-
-  def request_progress(params,len,total)
-    @request_progressed = true
-  end
-
-  def process(request, response)
-    @request_processed = true
-    response.start do |head,body|
-      body.write("test")
-    end
-  end
-
-end
-
-class RequestProgressTest < Test::Unit::TestCase
-  def setup
-    redirect_test_io do
-      @server = Mongrel::HttpServer.new("127.0.0.1", 9998)
-    end
-    @handler = UploadBeginHandler.new
-    @server.register("/upload", @handler)
-    @server.run
-  end
-
-  def teardown
-    @server.stop(true)
-  end
-
-  def test_begin_end_progress
-    Net::HTTP.get("localhost", "/upload", 9998)
-    assert @handler.request_began
-    assert @handler.request_progressed
-    assert @handler.request_processed
-  end
-
-  def call_and_assert_handlers_in_turn(handlers)
-    # reset all handlers
-    handlers.each { |h| h.reset }
-
-    # make the call
-    Net::HTTP.get("localhost", "/upload", 9998)
-
-    # assert that each one was fired
-    handlers.each { |h|
-      assert h.request_began && h.request_progressed && h.request_processed,
-        "Callbacks NOT fired for #{h}"
-    }
-  end
-
-  def test_more_than_one_begin_end_progress
-    handlers = [@handler]
-
-    second = UploadBeginHandler.new
-    @server.register("/upload", second)
-    handlers << second
-    call_and_assert_handlers_in_turn(handlers)
-
-    # check three handlers
-    third = UploadBeginHandler.new
-    @server.register("/upload", third)
-    handlers << third
-    call_and_assert_handlers_in_turn(handlers)
-
-    # remove handlers to make sure they've all gone away
-    @server.unregister("/upload")
-    handlers.each { |h| h.reset }
-    Net::HTTP.get("localhost", "/upload", 9998)
-    handlers.each { |h|
-      assert !h.request_began && !h.request_progressed && !h.request_processed
-    }
-
-    # re-register upload to the state before this test
-    @server.register("/upload", @handler)
-  end
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_response.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_response.rb
deleted file mode 100644 (file)
index 123ed98..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-include Mongrel
-
-class ResponseTest < Test::Unit::TestCase
-  
-  def test_response_headers
-    out = StringIO.new
-    resp = HttpResponse.new(out)
-    resp.status = 200
-    resp.header["Accept"] = "text/plain"
-    resp.header["X-Whatever"] = "stuff"
-    resp.body.write("test")
-    resp.finished
-
-    assert out.length > 0, "output didn't have data"
-  end
-
-  def test_response_200
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start do |head,out|
-      head["Accept"] = "text/plain"
-      out.write("tested")
-      out.write("hello!")
-    end
-
-    resp.finished
-    assert io.length > 0, "output didn't have data"
-  end
-
-  def test_response_duplicate_header_squash
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start do |head,out|
-      head["Content-Length"] = 30
-      head["Content-Length"] = 0
-    end
-
-    resp.finished
-
-    assert_equal io.length, 95, "too much output"
-  end
-
-
-  def test_response_some_duplicates_allowed
-    allowed_duplicates = ["Set-Cookie", "Set-Cookie2", "Warning", "WWW-Authenticate"]
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start do |head,out|
-      allowed_duplicates.each do |dup|
-        10.times do |i|
-          head[dup] = i
-        end
-      end
-    end
-
-    resp.finished
-
-    assert_equal io.length, 734, "wrong amount of output"
-  end
-
-  def test_response_404
-    io = StringIO.new
-
-    resp = HttpResponse.new(io)
-    resp.start(404) do |head,out|
-      head['Accept'] = "text/plain"
-      out.write("NOT FOUND")
-    end
-
-    resp.finished
-    assert io.length > 0, "output didn't have data"
-  end
-
-  def test_response_file
-    contents = "PLAIN TEXT\r\nCONTENTS\r\n"
-    require 'tempfile'
-    tmpf = Tempfile.new("test_response_file")
-    tmpf.binmode
-    tmpf.write(contents)
-    tmpf.rewind
-
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start(200) do |head,out|
-      head['Content-Type'] = 'text/plain'
-      resp.send_header
-      resp.send_file(tmpf.path)
-    end
-    io.rewind
-    tmpf.close
-    
-    assert io.length > 0, "output didn't have data"
-    assert io.read[-contents.length..-1] == contents, "output doesn't end with file payload"
-  end
-
-  def test_response_with_custom_reason
-    reason = "You made a bad request"
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start(400, false, reason) { |head,out| }
-    resp.finished
-
-    io.rewind
-    assert_match(/.* #{reason}$/, io.readline.chomp, "wrong custom reason phrase")
-  end
-
-  def test_response_with_default_reason
-    code = 400
-    io = StringIO.new
-    resp = HttpResponse.new(io)
-    resp.start(code) { |head,out| }
-    resp.finished
-
-    io.rewind
-    assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, io.readline.chomp, "wrong default reason phrase")
-  end
-
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_stats.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_stats.rb
deleted file mode 100644 (file)
index 404870a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-class StatsTest < Test::Unit::TestCase
-
-  def test_sampling_speed
-    out = StringIO.new
-
-    s = Mongrel::Stats.new("test")
-    t = Mongrel::Stats.new("time")
-
-    100.times { s.sample(rand(20)); t.tick }
-
-    s.dump("FIRST", out)
-    t.dump("FIRST", out)
-    
-    old_mean = s.mean
-    old_sd = s.sd
-
-    s.reset
-    t.reset
-    100.times { s.sample(rand(30)); t.tick }
-    
-    s.dump("SECOND", out)
-    t.dump("SECOND", out)
-    assert_not_equal old_mean, s.mean
-    assert_not_equal old_mean, s.sd    
-  end
-
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_uriclassifier.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_uriclassifier.rb
deleted file mode 100644 (file)
index 28af72c..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-include Mongrel
-
-class URIClassifierTest < Test::Unit::TestCase
-
-  def test_uri_finding
-    uri_classifier = URIClassifier.new
-    uri_classifier.register("/test", 1)
-    
-    script_name, path_info, value = uri_classifier.resolve("/test")
-    assert_equal 1, value
-    assert_equal "/test", script_name
-  end
-  
-  def test_root_handler_only
-    uri_classifier = URIClassifier.new
-    uri_classifier.register("/", 1)
-    
-    script_name, path_info, value = uri_classifier.resolve("/test")
-    assert_equal 1, value
-    assert_equal "/", script_name 
-    assert_equal "/test", path_info
-  end
-
-  def test_uri_prefix_ops
-    test = "/pre/fix/test"
-    prefix = "/pre"
-
-    uri_classifier = URIClassifier.new
-    uri_classifier.register(prefix,1)
-
-    script_name, path_info, value = uri_classifier.resolve(prefix)
-    script_name, path_info, value = uri_classifier.resolve(test)
-    assert_equal 1, value
-    assert_equal prefix, script_name
-    assert_equal test[script_name.length .. -1], path_info
-
-    assert uri_classifier.inspect
-    assert_equal prefix, uri_classifier.uris[0]
-  end
-
-  def test_not_finding
-    test = "/cant/find/me"
-    uri_classifier = URIClassifier.new
-    uri_classifier.register(test, 1)
-
-    script_name, path_info, value = uri_classifier.resolve("/nope/not/here")
-    assert_nil script_name
-    assert_nil path_info
-    assert_nil value
-  end
-
-  def test_exceptions
-    uri_classifier = URIClassifier.new
-
-    uri_classifier.register("/test", 1)
-    
-    failed = false
-    begin 
-      uri_classifier.register("/test", 1)
-    rescue => e
-      failed = true
-    end
-
-    assert failed
-
-    failed = false
-    begin
-      uri_classifier.register("", 1)
-    rescue => e
-      failed = true
-    end
-
-    assert failed
-  end
-
-
-  def test_register_unregister
-    uri_classifier = URIClassifier.new
-    
-    100.times do
-      uri_classifier.register("/stuff", 1)
-      value = uri_classifier.unregister("/stuff")
-      assert_equal 1, value
-    end
-
-    uri_classifier.register("/things",1)
-    script_name, path_info, value = uri_classifier.resolve("/things")
-    assert_equal 1, value
-
-    uri_classifier.unregister("/things")
-    script_name, path_info, value = uri_classifier.resolve("/things")
-    assert_nil value
-
-  end
-
-
-  def test_uri_branching
-    uri_classifier = URIClassifier.new
-    uri_classifier.register("/test", 1)
-    uri_classifier.register("/test/this",2)
-  
-    script_name, path_info, handler = uri_classifier.resolve("/test")
-    script_name, path_info, handler = uri_classifier.resolve("/test/that")
-    assert_equal "/test", script_name, "failed to properly find script off branch portion of uri"
-    assert_equal "/that", path_info
-    assert_equal 1, handler, "wrong result for branching uri"
-  end
-
-  def test_all_prefixing
-    tests = ["/test","/test/that","/test/this"]
-    uri = "/test/this/that"
-    uri_classifier = URIClassifier.new
-    
-    current = ""
-    uri.each_byte do |c|
-      current << c.chr
-      uri_classifier.register(current, c)
-    end
-    
-
-    # Try to resolve everything with no asserts as a fuzzing
-    tests.each do |prefix|
-      current = ""
-      prefix.each_byte do |c|
-        current << c.chr
-        script_name, path_info, handler = uri_classifier.resolve(current)
-        assert script_name
-        assert path_info
-        assert handler
-      end
-    end
-
-    # Assert that we find stuff
-    tests.each do |t|
-      script_name, path_info, handler = uri_classifier.resolve(t)
-      assert handler
-    end
-
-    # Assert we don't find stuff
-    script_name, path_info, handler = uri_classifier.resolve("chicken")
-    assert_nil handler
-    assert_nil script_name
-    assert_nil path_info
-  end
-
-
-  # Verifies that a root mounted ("/") handler resolves
-  # such that path info matches the original URI.
-  # This is needed to accommodate real usage of handlers.
-  def test_root_mounted
-    uri_classifier = URIClassifier.new
-    root = "/"
-    path = "/this/is/a/test"
-
-    uri_classifier.register(root, 1)
-
-    script_name, path_info, handler = uri_classifier.resolve(root)
-    assert_equal 1, handler
-    assert_equal root, path_info
-    assert_equal root, script_name
-
-    script_name, path_info, handler = uri_classifier.resolve(path)
-    assert_equal path, path_info
-    assert_equal root, script_name
-    assert_equal 1, handler
-  end
-
-  # Verifies that a root mounted ("/") handler
-  # is the default point, doesn't matter the order we use
-  # to register the URIs
-  def test_classifier_order
-    tests = ["/before", "/way_past"]
-    root = "/"
-    path = "/path"
-
-    uri_classifier = URIClassifier.new
-    uri_classifier.register(path, 1)
-    uri_classifier.register(root, 2)
-
-    tests.each do |uri|
-      script_name, path_info, handler = uri_classifier.resolve(uri)
-      assert_equal root, script_name, "#{uri} did not resolve to #{root}"
-      assert_equal uri, path_info
-      assert_equal 2, handler
-    end
-  end
-  
-  if ENV['BENCHMARK']
-    # Eventually we will have a suite of benchmarks instead of lamely installing a test
-    
-    def test_benchmark    
-
-      # This URI set should favor a TST. Both versions increase linearly until you hit 14 
-      # URIs, then the TST flattens out.
-      @uris = %w(
-        / 
-        /dag /dig /digbark /dog /dogbark /dog/bark /dug /dugbarking /puppy 
-        /c /cat /cat/tree /cat/tree/mulberry /cats /cot /cot/tree/mulberry /kitty /kittycat
-#        /eag /eig /eigbark /eog /eogbark /eog/bark /eug /eugbarking /iuppy 
-#        /f /fat /fat/tree /fat/tree/mulberry /fats /fot /fot/tree/mulberry /jitty /jittyfat
-#        /gag /gig /gigbark /gog /gogbark /gog/bark /gug /gugbarking /kuppy 
-#        /h /hat /hat/tree /hat/tree/mulberry /hats /hot /hot/tree/mulberry /litty /littyhat
-#        /ceag /ceig /ceigbark /ceog /ceogbark /ceog/cbark /ceug /ceugbarking /ciuppy 
-#        /cf /cfat /cfat/ctree /cfat/ctree/cmulberry /cfats /cfot /cfot/ctree/cmulberry /cjitty /cjittyfat
-#        /cgag /cgig /cgigbark /cgog /cgogbark /cgog/cbark /cgug /cgugbarking /ckuppy 
-#        /ch /chat /chat/ctree /chat/ctree/cmulberry /chats /chot /chot/ctree/cmulberry /citty /cittyhat
-      )
-      
-      @requests = %w(
-        /
-        /dig
-        /digging
-        /dogging
-        /dogbarking/
-        /puppy/barking
-        /c
-        /cat
-        /cat/shrub
-        /cat/tree
-        /cat/tree/maple
-        /cat/tree/mulberry/tree
-        /cat/tree/oak
-        /cats/
-        /cats/tree
-        /cod
-        /zebra
-      )
-    
-      @classifier = URIClassifier.new
-      @uris.each do |uri|
-        @classifier.register(uri, 1)
-      end
-      
-      puts "#{@uris.size} URIs / #{@requests.size * 10000} requests"
-  
-      Benchmark.bm do |x|
-        x.report do
-  #        require 'ruby-prof'
-  #        profile = RubyProf.profile do
-            10000.times do
-              @requests.each do |request|
-                @classifier.resolve(request)
-              end
-            end
-  #        end
-  #        File.open("profile.html", 'w') { |file| RubyProf::GraphHtmlPrinter.new(profile).print(file, 0) }
-        end
-      end          
-    end
-  end
-  
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_ws.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/test_ws.rb
deleted file mode 100644 (file)
index d239e18..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-require 'test/testhelp'
-
-include Mongrel
-
-class TestHandler < Mongrel::HttpHandler
-  attr_reader :ran_test
-
-  def process(request, response)
-    @ran_test = true
-    response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n")
-  end
-end
-
-
-class WebServerTest < Test::Unit::TestCase
-
-  def setup
-    @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n"
-    
-    redirect_test_io do
-      # We set num_processors=1 so that we can test the reaping code
-      @server = HttpServer.new("127.0.0.1", 9998, num_processors=1)
-    end
-    
-    @tester = TestHandler.new
-    @server.register("/test", @tester)
-    redirect_test_io do
-      @server.run 
-    end
-  end
-
-  def teardown
-    redirect_test_io do
-      @server.stop(true)
-    end
-  end
-
-  def test_simple_server
-    hit(['http://localhost:9998/test'])
-    assert @tester.ran_test, "Handler didn't really run"
-  end
-
-
-  def do_test(string, chunk, close_after=nil, shutdown_delay=0)
-    # Do not use instance variables here, because it needs to be thread safe
-    socket = TCPSocket.new("127.0.0.1", 9998);
-    request = StringIO.new(string)
-    chunks_out = 0
-
-    while data = request.read(chunk)
-      chunks_out += socket.write(data)
-      socket.flush
-      sleep 0.2
-      if close_after and chunks_out > close_after
-        socket.close
-        sleep 1
-      end
-    end
-    sleep(shutdown_delay)
-    socket.write(" ") # Some platforms only raise the exception on attempted write
-    socket.flush
-  end
-
-  def test_trickle_attack
-    do_test(@valid_request, 3)
-  end
-
-  def test_close_client
-    assert_raises IOError do
-      do_test(@valid_request, 10, 20)
-    end
-  end
-
-  def test_bad_client
-    redirect_test_io do
-      do_test("GET /test HTTP/BAD", 3)
-    end
-  end
-
-  def test_header_is_too_long
-    redirect_test_io do
-      long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n"
-      assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do
-        do_test(long, long.length/2, 10)
-      end
-    end
-  end
-
-  def test_num_processors_overload
-    redirect_test_io do
-      assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do
-        tests = [
-          Thread.new { do_test(@valid_request, 1) },
-          Thread.new { do_test(@valid_request, 10) },
-        ]
-
-        tests.each {|t| t.join}
-      end
-    end
-  end
-
-  def test_file_streamed_request
-    body = "a" * (Mongrel::Const::MAX_BODY * 2)
-    long = "GET /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body
-    do_test(long, Mongrel::Const::CHUNK_SIZE * 2 -400)
-  end
-
-end
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/testhelp.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/test/testhelp.rb
deleted file mode 100644 (file)
index 42ead2c..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw 
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
-# for more information.
-
-
-HERE = File.dirname(__FILE__)
-%w(lib ext bin test).each do |dir| 
-  $LOAD_PATH.unshift "#{HERE}/../#{dir}"
-end
-
-require 'rubygems'
-require 'test/unit'
-require 'net/http'
-require 'timeout'
-require 'cgi/session'
-require 'fileutils'
-require 'benchmark'
-require 'digest/sha1'
-require 'uri'
-require 'stringio'
-require 'pp'
-
-require 'mongrel'
-require 'mongrel/stats'
-
-if ENV['DEBUG']
-  require 'ruby-debug'
-  Debugger.start
-end
-
-def redirect_test_io
-  orig_err = STDERR.dup
-  orig_out = STDOUT.dup
-  STDERR.reopen("test_stderr.log")
-  STDOUT.reopen("test_stdout.log")
-
-  begin
-    yield
-  ensure
-    STDERR.reopen(orig_err)
-    STDOUT.reopen(orig_out)
-  end
-end
-    
-# Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
-# HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
-def hit(uris)
-  results = []
-  uris.each do |u|
-    res = nil
-
-    if u.kind_of? String
-      res = Net::HTTP.get(URI.parse(u))
-    else
-      url = URI.parse(u[0])
-      res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
-    end
-
-    assert res != nil, "Didn't get a response: #{u}"
-    results << res
-  end
-
-  return results
-end
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/tools/trickletest.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel-1.1.5-x86-mswin32-60/tools/trickletest.rb
deleted file mode 100644 (file)
index e19ed71..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'socket'
-require 'stringio'
-
-def do_test(st, chunk)
-  s = TCPSocket.new('127.0.0.1',ARGV[0].to_i);
-  req = StringIO.new(st)
-  nout = 0
-  randstop = rand(st.length / 10)
-  STDERR.puts "stopping after: #{randstop}"
-
-  begin
-    while data = req.read(chunk)
-      nout += s.write(data)
-      s.flush
-      sleep 0.1
-      if nout > randstop
-        STDERR.puts "BANG! after #{nout} bytes."
-        break
-      end
-    end
-  rescue Object => e
-    STDERR.puts "ERROR: #{e}"
-  ensure
-    s.close
-  end
-end
-
-content = "-" * (1024 * 240)
-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}"
-
-puts "length: #{content.length}"
-
-threads = []
-ARGV[1].to_i.times do 
-  t = Thread.new do
-    size = 100
-    puts ">>>> #{size} sized chunks"
-    do_test(st, size)
-  end
-
-  t.abort_on_exception = true
-  threads << t
-end
-
-threads.each {|t|  t.join}
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/CHANGELOG b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/CHANGELOG
deleted file mode 100644 (file)
index 5112e7d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-* 0.3.4 *
-    
-    * Strict Gem dependencies for mongrel_service. This version is compatible
-      only with mongrel 1.0.x, 1.1.x and with win32-service 0.5.x.
-
-* 0.3.3 *
-    
-    * Properly display package/gem version for mongrel_service. Closes #13823.
-    
-    * Updated ServiceFB to r80 to solve issue when compiling with FB > 0.17.
-    
-* 0.3.2 *
-    
-    * Solved detection of parent process at ServiceFB level
-      (solves the x64 Windows issues).
-    
-    * Upgraded to ServiceFB 'trunk' (but pistoned it, just in case).
-    
-    * Fixed problems with ruby installations outside PATH or inside folders with spaces.
-    
-    * Activate FB pedantic warnings by default (is really useful).
-    
-* 0.3.1 *
-    
-    * Single Service (SingleMongrel) object type implemented.
-    
-    * Updated Rakefile to reflect the new building steps.
-    
-    * Removed SendSignal, too hackish for my taste, replaced with complete FB solution.
-    
-    * Added basic Process monitoring and re-spawning.
-    
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/COPYING b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/COPYING
deleted file mode 100644 (file)
index 789d76e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2006 Luis Lavena, luislavena@gmail.com\r
-\r
-Permission is hereby granted, free of charge, to any person obtaining\r
-a copy of this software and associated documentation files (the\r
-"Software"), to deal in the Software without restriction, including\r
-without limitation the rights to use, copy, modify, merge, publish,\r
-distribute, sublicense, and/or sell copies of the Software, and to\r
-permit persons to whom the Software is furnished to do so, subject to\r
-the following conditions:\r
-\r
-The above copyright notice and this permission notice shall be\r
-included in all copies or substantial portions of the Software.\r
-\r
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/LICENSE b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/LICENSE
deleted file mode 100644 (file)
index e5a926e..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
-<zedshaw at zedshaw dot com> and contributors. You can redistribute it 
-and/or modify it under either the terms of the GPL2 or the conditions below:
-
-1. You may make and give away verbatim copies of the source form of the
-   software without restriction, provided that you duplicate all of the
-   original copyright notices and associated disclaimers.
-
-2. You may modify your copy of the software in any way, provided that
-   you do at least ONE of the following:
-
-     a) place your modifications in the Public Domain or otherwise make them
-     Freely Available, such as by posting said modifications to Usenet or an
-     equivalent medium, or by allowing the author to include your
-     modifications in the software.
-
-     b) use the modified software only within your corporation or
-        organization.
-
-     c) rename any non-standard executables so the names do not conflict with
-     standard executables, which must also be provided.
-
-     d) make other distribution arrangements with the author.
-
-3. You may distribute the software in object code or executable
-   form, provided that you do at least ONE of the following:
-
-     a) distribute the executables and library files of the software,
-     together with instructions (in the manual page or equivalent) on where
-     to get the original distribution.
-
-     b) accompany the distribution with the machine-readable source of the
-     software.
-
-     c) give non-standard executables non-standard names, with
-        instructions on where to get the original software distribution.
-
-     d) make other distribution arrangements with the author.
-
-4. You may modify and include the part of the software into any other
-   software (possibly commercial).  But some files in the distribution
-   are not written by the author, so that they are not under this terms.
-
-5. The scripts and library files supplied as input to or produced as 
-   output from the software do not automatically fall under the
-   copyright of the software, but belong to whomever generated them, 
-   and may be sold commercially, and may be aggregated with this
-   software.
-
-6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-   PURPOSE.
-
-
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/Manifest b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/Manifest
deleted file mode 100644 (file)
index d5351fa..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-bin/mongrel_service.exe
-tools/freebasic.rb
-TODO
-resources/defaults.yaml
-README
-native/mongrel_service.bi
-native/mongrel_service.bas
-native/console_process.bi
-native/console_process.bas
-native/_debug.bi
-LICENSE
-lib/ServiceFB/ServiceFB_Utils.bi
-lib/ServiceFB/ServiceFB_Utils.bas
-lib/ServiceFB/ServiceFB.bi
-lib/ServiceFB/ServiceFB.bas
-lib/ServiceFB/_utils_internals.bi
-lib/ServiceFB/_internals.bi
-lib/mongrel_service/init.rb
-COPYING
-CHANGELOG
-Manifest
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/README b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/README
deleted file mode 100644 (file)
index 45bf9c8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-== Mongrel Native Win32 Service Plugin\r
-\r
-This plugin offer native win32 services for rails. This replace mongrel_rails_service.\r
-It will work like before, with this this syntax when calling mongrel_rails:\r
-\r
-service::install\r
-service::remove\r
-service::update\r
-\r
-= Author:\r
-  Luis Lavena\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/TODO b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/TODO
deleted file mode 100644 (file)
index e39b38c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Legend:\r
-[ ] not done\r
-[X] done\r
-[+] in progess\r
-\r
-### General\r
-[ ] Add more documentation about services and requirements\r
-[ ] Add process monitoring.\r
-\r
-### Dependencies\r
-[+] Remove win32/service extension dependency (instead of relying in the non-official 0.5.0 one).\r
-  [ ] Add service management (from ServiceFB) to install and remove services.\r
-    \r
-### SingleMongrel\r
-[+] Sanitize SingleMongrel and document the functions.\r
-\r
-### ClusterMongrel\r
-[ ] Parse mongrel_cluster configuration file (yaml) looking for port information\r
-  [ ] Reimplent SingleMongrel for ClusterMongrel, splitting source files for better organization.\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/bin/mongrel_service.exe b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/bin/mongrel_service.exe
deleted file mode 100644 (file)
index 769b7ff..0000000
Binary files a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/bin/mongrel_service.exe and /dev/null differ
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bas b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bas
deleted file mode 100644 (file)
index 8364867..0000000
+++ /dev/null
@@ -1,650 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#include once "ServiceFB.bi"\r
-#include once "_internals.bi"\r
-\r
-namespace fb\r
-namespace svc\r
-    '# I started this as simple, unique service served from one process\r
-    '# but the idea of share the same process space (and reduce resources use) was good.\r
-    '# to do that, I needed a references table (similar to service_table, but we will\r
-    '# hold the ServiceProcess registered by ServiceHost (the multi services host).\r
-    '# also, I needed a locking mechanism to avoid problems of two calls changing the table\r
-    '# at the same time.\r
-    dim shared _svc_references as ServiceProcess ptr ptr\r
-    dim shared _svc_references_count as integer\r
-    dim shared _svc_references_lock as any ptr\r
-    \r
-    \r
-    '#####################\r
-    '# ServiceProcess\r
-    '# ctor()\r
-    constructor ServiceProcess()\r
-        constructor("NewServiceProcess")\r
-    end constructor\r
-    \r
-    \r
-    '# ctor(name)\r
-    constructor ServiceProcess(byref new_name as string)\r
-        _dprint("ServiceProcess(new_name)")\r
-        '# assign the service name\r
-        this.name = new_name\r
-\r
-        '# initialize the status structure\r
-        with this._svcStatus\r
-            .dwServiceType = SERVICE_WIN32_OWN_PROCESS\r
-            .dwCurrentState = SERVICE_STOPPED\r
-            .dwControlsAccepted = (SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_SHUTDOWN)\r
-            .dwWin32ExitCode = NO_ERROR\r
-            .dwServiceSpecificExitCode = NO_ERROR\r
-            .dwCheckPoint = 0\r
-            .dwWaitHint = 0\r
-        end with\r
-        \r
-        '# use a state placeholder\r
-        this.state = this._svcStatus.dwCurrentState\r
-        \r
-        '# disable shared process by default\r
-        this.shared_process = FALSE\r
-        \r
-        '# create the stop event\r
-        this._svcStopEvent = CreateEvent( 0, FALSE, FALSE, 0 )\r
-        _dprint("ServiceProcess(new_name) done")\r
-    end constructor\r
-    \r
-    \r
-    '# dtor()\r
-    destructor ServiceProcess()\r
-        _dprint("ServiceProcess() destructor")\r
-        '# safe to destroy it. anyway, just checking\r
-        with this\r
-            .onInit = 0\r
-            .onStart = 0\r
-            .onStop = 0\r
-            .onPause = 0\r
-            .onContinue = 0\r
-            ._threadHandle = 0\r
-            CloseHandle(._svcStopEvent)\r
-        end with\r
-        _dprint("ServiceProcess() destructor done")\r
-    end destructor\r
-    \r
-    \r
-    '# for single process, here I create the references table and then \r
-    '# delegate control to _run() which will call the service control dispatcher\r
-    sub ServiceProcess.Run()\r
-        _dprint("ServiceProcess.Run()")\r
-        \r
-        '# add the unique reference\r
-        _add_to_references(this)\r
-        \r
-        '# delegate control to _run()\r
-        _run()\r
-        \r
-        _dprint("ServiceProcess.Run() done")\r
-    end sub\r
-    \r
-    \r
-    '# I use this method to simplify changing the service state \r
-    '# notification to the service manager.\r
-    '# is needed to set dwControlsAccepted = 0 if state is SERVICE_*_PENDING\r
-    '# also, StillAlive() call it to set the checkpoint and waithint\r
-    '# to avoid SCM shut us down.\r
-    '# is not for the the end-user (*you*) to access it, but implemented in this\r
-    '# way to reduce needed to pass the right service reference each time\r
-    sub ServiceProcess.UpdateState(byval state as DWORD, byval checkpoint as integer = 0, byval waithint as integer = 0)\r
-        _dprint("ServiceProcess.UpdateState()")\r
-        '# set the state\r
-        select case state\r
-            '# if the service is starting or stopping, I must disable the option to accept\r
-            '# other controls form SCM.\r
-            case SERVICE_START_PENDING, SERVICE_STOP_PENDING:\r
-                this._svcStatus.dwControlsAccepted = 0\r
-            \r
-            '# in this case, running or paused, stop and shutdown must be available\r
-            '# also, we must check here if our service is capable of pause/continue Ã§\r
-            '# functionality and allow them (or not).\r
-            case SERVICE_RUNNING, SERVICE_PAUSED:\r
-                this._svcStatus.dwControlsAccepted = (SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_SHUTDOWN)\r
-                \r
-                '# from start, the service accept stop and shutdown (see ctor(name)).\r
-                '# configure the accepted controls.\r
-                '# Pause and Continue only will be enabled if you setup onPause and onContinue\r
-                if not (this.onPause = 0) and _\r
-                    not (this.onContinue = 0) then\r
-                    this._svcStatus.dwControlsAccepted or= SERVICE_ACCEPT_PAUSE_CONTINUE\r
-                end if\r
-                \r
-        end select\r
-        \r
-        '# set the structure status\r
-        '# also the property\r
-        this._svcStatus.dwCurrentState = state\r
-        this.state = state\r
-        \r
-        '# set checkpoint and waithint\r
-        this._svcStatus.dwCheckPoint = checkpoint\r
-        this._svcStatus.dwWaitHint = waithint\r
-        \r
-        '# call the API\r
-        '# only we will call is _svcHandle is valid\r
-        '# this will allow use of UpdateState (and StillAlive) from console\r
-        if not (this._svcHandle = 0) then\r
-            _dprint("SetServiceStatus() API")\r
-            SetServiceStatus(this._svcHandle, @this._svcStatus)\r
-        end if\r
-        _dprint("ServiceProcess.UpdateState() done")\r
-    end sub\r
-    \r
-    \r
-    '# use StillAlive() method when performing lengthly tasks during onInit or onStop\r
-    '# (if they take too much time).\r
-    '# by default we set a wait hint gap of 10 seconds, but you could specify how many\r
-    '# you could specify how many seconds more will require your *work*\r
-    sub ServiceProcess.StillAlive(byval waithint as integer = 10)\r
-        dim as integer checkpoint\r
-\r
-        _dprint("ServiceProcess.StillAlive()")\r
-        '# start or stop pending?\r
-        if (this._svcStatus.dwCurrentState = SERVICE_START_PENDING) or _\r
-            (this._svcStatus.dwCurrentState = SERVICE_STOP_PENDING) then\r
-                with this\r
-                    checkpoint = this._svcStatus.dwCheckPoint\r
-                    checkpoint += 1\r
-                    .UpdateState(._svcStatus.dwCurrentState, checkpoint, (waithint * 1000))\r
-                end with\r
-        end if\r
-        _dprint("ServiceProcess.StillAlive() done")\r
-    end sub\r
-    \r
-    \r
-    '# call_onStart() is a wrapper around the new limitation of threadcreate\r
-    '# sub used as pointers in threadcreate must conform the signature\r
-    sub ServiceProcess.call_onStart(byval any_service as any ptr)\r
-        var service = cast(ServiceProcess ptr, any_service)\r
-        service->onStart(*service)\r
-    end sub\r
-    \r
-    '#####################\r
-    '# ServiceHost\r
-    '# ctor()\r
-    '# currently isn't needed, why I defined it?\r
-    constructor ServiceHost()\r
-        _dprint("ServiceHost()")\r
-        _dprint("ServiceHost() done")\r
-    end constructor\r
-    \r
-    \r
-    '# dtor()\r
-    '# currently isn't needed, why I defined it?\r
-    destructor ServiceHost()\r
-        _dprint("ServiceHost() destructor")\r
-        _dprint("ServiceHost() destructor done")\r
-    end destructor\r
-    \r
-    \r
-    '# using Add() will register an already initialized service into the references\r
-    '# table, which will be used later to launch and control the different services\r
-    '# we should be careful when handling references, so for that reference_lock is\r
-    '# provided ;-)\r
-    sub ServiceHost.Add(byref service as ServiceProcess)\r
-        _dprint("ServiceHost.Add()")\r
-        \r
-        '# add the service reference to the references table\r
-        '# get the new count as result, so\r
-        '# increment the local counter\r
-        this.count = _add_to_references(service)\r
-        \r
-        _dprint("ServiceHost.Add() done")\r
-    end sub\r
-    \r
-    \r
-    '# ServiceHost.Run() is just a placeholder, it delegates control to _run()\r
-    '# pretty simple, but still must be present to simplify user interaction.\r
-    sub ServiceHost.Run()\r
-        _dprint("ServiceHost.Run()")\r
-        \r
-        '# the longest, hard coded function in the world!\r
-        '# just kidding\r
-        _run()\r
-        \r
-        _dprint("ServiceHost.Run() done")\r
-    end sub\r
-    \r
-    \r
-    '# the purpose of this sub is provide a generic service creation and running\r
-    '# this is be called from exisitng ServiceProcess and ServiceHost.\r
-    '# this construct the SERVICE_TABLE_ENTRY based on the the references table,\r
-    '# which will be sent to StartServiceCtrlDispatcher()\r
-    private sub _run()\r
-        dim ServiceTable(_svc_references_count) as SERVICE_TABLE_ENTRY\r
-        dim idx as integer\r
-        \r
-        _dprint("_run()")\r
-        \r
-        _dprint("creating service table for " + str(_svc_references_count) + " services")\r
-        for idx = 0 to (_svc_references_count - 1)\r
-            '# we take the service name from the references and set as ServiceMain the same\r
-            '# _main() routine for all the services\r
-            ServiceTable(idx) = type<SERVICE_TABLE_ENTRY>(strptr(_svc_references[idx]->name), @_main)\r
-            _dprint(str(idx) + ": " + _svc_references[idx]->name)\r
-        next idx\r
-        '# last member of the table must be null\r
-        ServiceTable(_svc_references_count) = type<SERVICE_TABLE_ENTRY>(0, 0)\r
-        _dprint("service table created")\r
-        \r
-        '# start the dispatcher\r
-        _dprint("start service dispatcher")\r
-        StartServiceCtrlDispatcher( @ServiceTable(0) )\r
-        \r
-        _dprint("_run() done")\r
-    end sub\r
-    \r
-    \r
-    '# this sub is fired by StartServiceCtrlDispatcher in another thread.\r
-    '# because it is a global _main for all the services in the table, looking up\r
-    '# in the references for the right service is needed prior registering its\r
-    '# control handler.\r
-    private sub _main(byval argc as DWORD, byval argv as LPSTR ptr)\r
-        dim success as integer\r
-        dim service as ServiceProcess ptr\r
-        dim run_mode as string\r
-        dim service_name as string\r
-        dim commandline as string\r
-        dim param_line as string\r
-        dim temp as string\r
-        \r
-        _dprint("_main()")\r
-        \r
-        '# debug dump of argc and argv\r
-        dim idx as integer = 0\r
-        for idx = 0 to (argc - 1)\r
-            _dprint(str(idx) + ": " + *argv[idx])\r
-        next idx\r
-        \r
-        '# retrieve all the information (mode, service name and command line\r
-        _build_commandline(run_mode, service_name, commandline)\r
-        service = _find_in_references(service_name)\r
-        \r
-        '# build parameter line (passed from SCM)\r
-        if (argc > 1) then\r
-            param_line = ""\r
-            for idx = 1 to (argc - 1)\r
-                temp = *argv[idx]\r
-                if (instr(temp, chr(32)) > 0) then\r
-                    param_line += """" + temp + """"\r
-                else\r
-                    param_line += temp\r
-                end if\r
-                param_line += " "\r
-            next idx\r
-        end if\r
-        \r
-        '# parameters passed using SCM have priority over ImagePath ones\r
-        if not (len(param_line) = 0) then\r
-            commandline = param_line\r
-        end if\r
-        \r
-        '# a philosofical question: to run or not to run?\r
-        if not (service = 0) then\r
-            _dprint("got a valid service reference")\r
-            _dprint("real service name: " + service->name)\r
-            \r
-            '# pass to the service the commandline\r
-            _dprint("passing service commandline: " + commandline)\r
-            service->commandline = commandline\r
-            \r
-            '# ok, its a service!, its alive!\r
-            '# register his ControlHandlerEx\r
-            _dprint("register control handler ex")\r
-            service->_svcHandle = RegisterServiceCtrlHandlerEx(strptr(service_name), @_control_ex, cast(LPVOID, service))\r
-            \r
-            '# check if evething is done right\r
-            if not (service->_svcHandle = 0) then\r
-                '# now, we are a single service or a bunch, like the bradys?\r
-                if (_svc_references_count > 1) then\r
-                    '# determine if we share or not the process\r
-                    if (service->shared_process = FALSE) then\r
-                        service->_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS\r
-                    else\r
-                        '# this mean we will be sharing... hope neighbors don't crash the house!\r
-                        service->_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS\r
-                    end if\r
-                else\r
-                    '# ok, we have a full house (ehem, process) for us only!\r
-                    service->_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS\r
-                end if\r
-                \r
-                '# START_PENDING\r
-                _dprint("service start pending")\r
-                service->UpdateState(SERVICE_START_PENDING)\r
-                \r
-                '# now delegate to the long running initialization if it exist.\r
-                if not (service->onInit = 0) then\r
-                    _dprint("pass control to lengthly initialization")\r
-                    success = service->onInit(*service)\r
-                else\r
-                    '# if no onInit was defined (maybe you don't need it?)\r
-                    '# we should simulate it was successful to proceed\r
-                    success = (-1)\r
-                end if\r
-                _dprint("onInit result: " + str(success))\r
-                \r
-                '# check if everything is ok\r
-                '# if onInit showed problems, 0 was returned and service must not continue\r
-                if not (success = 0) then\r
-                    '# SERVICE_RUNNING\r
-                    '# we must launch the onStart as thread, but first setting state as running\r
-                    service->UpdateState(SERVICE_RUNNING)\r
-                    if not (service->onStart = 0) then\r
-                        _dprint("dispatch onStart() as new thread")\r
-                        service->_threadHandle = threadcreate(@ServiceProcess.call_onStart, service)\r
-                        '# my guess? was a hit!\r
-                    end if\r
-                    \r
-                    '# now that we are out of onStart thread, check if actually hit the stop sign\r
-                    _dprint("waiting for stop signal")\r
-                    do\r
-                        '# do nothing ...\r
-                        '# but not too often!\r
-                    loop while (WaitForSingleObject(service->_svcStopEvent, 100) = WAIT_TIMEOUT)\r
-                    \r
-                    '# now, wait for the thread (anyway, I hope it will be checking this.state, right?)\r
-                    '# we should do this, or actualy jump and wait for StopEvent?\r
-                    _dprint("waiting for onStart() thread to finish")\r
-                    threadwait(service->_threadHandle)\r
-                end if\r
-                \r
-                '# if we reach here, that means the service is not running, and the onStop was performed\r
-                '# so no more chat, stop it one and for all!\r
-                '# set SERVICE_STOPPED (just checking)\r
-                _dprint("service stopped")\r
-                service->UpdateState(SERVICE_STOPPED)\r
-            end if\r
-            \r
-            '# ok, we are done!\r
-        end if\r
-        \r
-        _dprint("_main() done")\r
-    end sub\r
-    \r
-    \r
-    '# this sub is used by _main when registering the ControlHandler for this service \r
-    '# (as callback from service manager).\r
-    '# we process each control codes and perform the actions using the pseudo-events (callbacks)\r
-    '# also we use lpContext to get the right reference when _main registered the control handler.\r
-    private function _control_ex(byval dwControl as DWORD, byval dwEventType as DWORD, byval lpEventData as LPVOID, byval lpContext as LPVOID) as DWORD\r
-        dim result as DWORD\r
-        dim service as ServiceProcess ptr\r
-        \r
-        _dprint("_control_ex()")\r
-        \r
-        '# we get a reference form the context\r
-        service = cast(ServiceProcess ptr, lpContext)\r
-        \r
-        '# show if the service reference is valid?\r
-        _dprint("service name: " + service->name)\r
-        \r
-        select case dwControl\r
-            case SERVICE_CONTROL_INTERROGATE:\r
-                '# we are running, so what we should do here?\r
-                _dprint("interrogation signal received")\r
-                '# in case we get a interrogation, we always should answer this way.\r
-                result = NO_ERROR\r
-                \r
-            case SERVICE_CONTROL_SHUTDOWN, SERVICE_CONTROL_STOP:\r
-                _dprint("stop signal received")\r
-                '# ok, service manager requested us to stop.\r
-                '# we must call onStop if was defined.\r
-                service->UpdateState(SERVICE_STOP_PENDING)\r
-                if not (service->onStop = 0) then\r
-                    _dprint("pass control to onStop()")\r
-                    service->onStop(*service)\r
-                end if\r
-                '# now signal the stop event so _main could take care of the rest.\r
-                _dprint("signal stop event")\r
-                SetEvent(service->_svcStopEvent)\r
-                \r
-            case SERVICE_CONTROL_PAUSE:\r
-                _dprint("pause signal received")\r
-                '# we must check if we could answer to the request.\r
-                if not (service->onPause = 0) and _\r
-                    not (service->onContinue = 0) then\r
-                    \r
-                    '# just to be sure\r
-                    if not (service->onPause = 0) then\r
-                        service->UpdateState(SERVICE_PAUSE_PENDING)\r
-                        \r
-                        _dprint("pass control to onPause()")\r
-                        service->onPause(*service)\r
-                        \r
-                        service->UpdateState(SERVICE_PAUSED)\r
-                        _dprint("service paused")\r
-                    end if\r
-                    result = NO_ERROR\r
-                    \r
-                else\r
-                    '# ok, our service didn't support pause or continue\r
-                    '# tell the service manager about that!\r
-                    result = ERROR_CALL_NOT_IMPLEMENTED\r
-                end if\r
-                \r
-            case SERVICE_CONTROL_CONTINUE:\r
-                _dprint("continue signal received")\r
-                '# we should resume from a paused state\r
-                '# we must check if we could answer to the request.\r
-                if not (service->onPause = 0) and _\r
-                    not (service->onContinue = 0) then\r
-                    \r
-                    '# just to be sure\r
-                    if not (service->onPause = 0) then\r
-                        service->UpdateState(SERVICE_CONTINUE_PENDING)\r
-                        \r
-                        _dprint("pass control to onContinue()")\r
-                        service->onContinue(*service)\r
-                        \r
-                        service->UpdateState(SERVICE_RUNNING)\r
-                        _dprint("service running")\r
-                    end if\r
-                    result = NO_ERROR\r
-                    \r
-                else\r
-                    '# ok, our service didn't support pause or continue\r
-                    '# tell the service manager about that!\r
-                    result = ERROR_CALL_NOT_IMPLEMENTED\r
-                end if\r
-                \r
-            case else:\r
-                result = NO_ERROR\r
-        end select\r
-        \r
-        _dprint("_control_ex() done")\r
-        return result\r
-    end function\r
-    \r
-    \r
-    '# add_to_references is a helper used to reduce code duplication (DRY).\r
-    '# here is used a lock around _svc_references to avoid two threads try change the\r
-    '# reference count (just in case).\r
-    function _add_to_references(byref service as ServiceProcess) as integer\r
-        _dprint("_add_to_references()")\r
-        \r
-        '# get a lock before even think touch references!\r
-        mutexlock(_svc_references_lock)\r
-        \r
-        '# now, reallocate space\r
-        _svc_references_count += 1\r
-        _svc_references = reallocate(_svc_references, sizeof(ServiceProcess ptr) * _svc_references_count)\r
-        \r
-        '# put the reference of this service into the table\r
-        _svc_references[(_svc_references_count - 1)] = @service\r
-        \r
-        '# ok, done, unlock our weapons! ;-)\r
-        mutexunlock(_svc_references_lock)\r
-        \r
-        _dprint("_add_to_references() done")\r
-        '# return the new references count\r
-        return _svc_references_count\r
-    end function\r
-    \r
-    \r
-    '# find_in_references is used by _main to lookup for the specified service in \r
-    '# references table. \r
-    function _find_in_references(byref service_name as string) as ServiceProcess ptr\r
-        dim result as ServiceProcess ptr\r
-        dim item as ServiceProcess ptr\r
-        dim idx as integer\r
-        \r
-        _dprint("_find_in_references()")\r
-        \r
-        '# we start with a pesimistic idea ;-)\r
-        result = 0\r
-        \r
-        for idx = 0 to (_svc_references_count - 1)\r
-            '# hold a reference to the item\r
-            item = _svc_references[idx]\r
-            \r
-            '# compare if we have a match\r
-            if (service_name = item->name) then\r
-                result = item\r
-                exit for\r
-            end if\r
-        next idx\r
-        \r
-        _dprint("_find_in_references() done")\r
-        '# return the found (or not) reference\r
-        return result\r
-    end function\r
-    \r
-    \r
-    '# namespace constructor\r
-    '# first we must create the mutex to be used with references\r
-    private sub _initialize() constructor\r
-        _dprint("_initialize() constructor")\r
-        '# we do this in case was already defined... don't know the situation,\r
-        '# just to be sure\r
-        if (_svc_references_lock = 0) then\r
-            _svc_references_lock = mutexcreate()\r
-            \r
-            '# also initialize our count :-)\r
-            _svc_references_count = 0\r
-        end if\r
-        \r
-        _dprint("_initialize() constructor done")\r
-    end sub\r
-    \r
-    \r
-    '# namespace destructor\r
-    private sub _terminate() destructor\r
-        _dprint("_terminate() destructor")\r
-        '# to avoid removing everything, we must lock to the references\r
-        mutexlock(_svc_references_lock)\r
-        \r
-        '# destroy our refernces allocated memory!\r
-        deallocate(_svc_references)\r
-        \r
-        '# unlock the mutex and destroy it too.\r
-        mutexunlock(_svc_references_lock)\r
-        mutexdestroy(_svc_references_lock)\r
-        \r
-        _dprint("_terminate() destructor done")\r
-    end sub\r
-    \r
-    \r
-    '# command line builder (helper)\r
-    '# this is used to gather information about:\r
-    '# mode (if present)\r
-    '# valid service name (after lookup in the table)\r
-    '# command line to be passed to service\r
-    sub _build_commandline(byref mode as string, byref service_name as string, byref commandline as string)\r
-        dim result_mode as string\r
-        dim result_name as string\r
-        dim result_cmdline as string\r
-        dim service as ServiceProcess ptr\r
-        dim idx as integer\r
-        dim temp as string\r
-        \r
-        idx = 1\r
-        '# first, determine if mode is pressent in commandline, must me command(1)\r
-        temp = lcase(command(idx))\r
-        \r
-        if (temp = "console") or _\r
-            (temp = "manage") then\r
-            result_mode = temp\r
-            idx += 1\r
-        end if\r
-        \r
-        '# now, check if service name is present\r
-        temp = command(idx)\r
-        \r
-        '# its present?\r
-        if (len(temp) > 0) then\r
-            '# lookup in references table\r
-            service = _find_in_references(temp)\r
-            if not (service = 0) then\r
-                '# was found, so must be valid\r
-                result_name = temp\r
-                '# adjust start index for cmdline\r
-                idx += 1\r
-            end if\r
-        end if\r
-\r
-        '# is service valid?\r
-        '# its really needed?\r
-        if (service = 0) then\r
-            if (_svc_references_count = 1) then\r
-                '# no, get the first one\r
-                service = _svc_references[0]\r
-                result_name = service->name\r
-                '# adjust start index for cmdline\r
-            else\r
-                '# this is needed!\r
-                result_name = ""\r
-            end if\r
-        end if\r
-        \r
-        result_cmdline = ""\r
-        \r
-        temp = command(idx)\r
-        do while (len(temp) > 0)\r
-            if (instr(temp, chr(32)) > 0) then\r
-                '# properly quote parameters with spaces\r
-                result_cmdline += """" + temp + """"\r
-            else\r
-                result_cmdline += temp\r
-            end if\r
-            result_cmdline += " "\r
-            idx += 1\r
-            \r
-            temp = command(idx)\r
-        loop\r
-        \r
-        '# now, return the results\r
-        mode = result_mode\r
-        service_name = result_name\r
-        commandline = result_cmdline\r
-    end sub\r
-    \r
-    \r
-    '# ### DEBUG ###\r
-    '# just for debuging purposes \r
-    '# (will be removed in the future when Loggers get implemented)\r
-#ifdef SERVICEFB_DEBUG_LOG\r
-    sub _dprint(byref message as string)\r
-        dim handle as integer\r
-        \r
-        handle = freefile\r
-        open EXEPATH + "\servicefb.log" for append as #handle\r
-        \r
-        print #handle, message\r
-        \r
-        close #handle\r
-    end sub\r
-#endif\r
-end namespace   '# fb.svc\r
-end namespace   '# fb\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB.bi
deleted file mode 100644 (file)
index dc0acec..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#if __FB_VERSION__ < "0.17"\r
-#error ServiceFB is designed to compile with FreeBASIC version "0.17"\r
-#else\r
-\r
-#ifndef __FB_WIN32__\r
-#error Platform unsupported. Compiling ServiceFB requires Windows platform.\r
-#else\r
-\r
-#ifndef __ServiceFB_bi__\r
-#define __ServiceFB_bi__\r
-\r
-#include once "windows.bi"\r
-#inclib "advapi32"\r
-\r
-namespace fb\r
-namespace svc   '# fb.svc\r
-#ifdef SERVICEFB_DEBUG_LOG\r
-    '# debug print\r
-    declare sub _dprint(byref as string)\r
-#else\r
-    #define _dprint(message)\r
-#endif\r
-    \r
-    '# service states used by end user with 'state' property\r
-    enum ServiceStateEnum\r
-        Running = SERVICE_RUNNING\r
-        Paused = SERVICE_PAUSED\r
-        Stopped = SERVICE_STOPPED\r
-    end enum\r
-    \r
-    \r
-    '# ServiceProcess type (object)\r
-    '# use this to create new services and reference the on*() methods to perform the related\r
-    '# tasks.\r
-    type ServiceProcess\r
-        '# ctor/dtor\r
-        declare constructor()\r
-        declare constructor(byref as string)\r
-        declare destructor()\r
-        \r
-        '# methods (public)\r
-        declare sub Run()\r
-        declare sub StillAlive(byval as integer = 10)\r
-        \r
-        '# helper methods (private)\r
-        declare sub UpdateState(byval as DWORD, byval as integer = 0, byval as integer = 0)\r
-        \r
-        '# pseudo-events\r
-        '# for onInit you should return FALSE (0) in case you want to abort\r
-        '# service initialization.\r
-        '# If everything was ok, then return TRUE (-1)\r
-        onInit          as function(byref as ServiceProcess) as integer\r
-        onStart         as sub(byref as ServiceProcess)\r
-        onStop          as sub(byref as ServiceProcess)\r
-        onPause         as sub(byref as ServiceProcess)\r
-        onContinue      as sub(byref as ServiceProcess)\r
-        \r
-        '# call_* are used to avoid the warning arround ThreadCreate\r
-        declare static sub call_onStart(byval as any ptr)\r
-        \r
-        '# properties (public)\r
-        name            as string\r
-        description     as string\r
-        state           as ServiceStateEnum\r
-        commandline     as string                   '# TODO\r
-        shared_process  as integer\r
-        \r
-        '# properties (private)\r
-        _svcStatus      as SERVICE_STATUS\r
-        _svcHandle      as SERVICE_STATUS_HANDLE\r
-        _svcStopEvent   as HANDLE\r
-        _threadHandle   as any ptr\r
-    end type\r
-    \r
-    \r
-    '# ServiceHost type (object)\r
-    '# use this, beside ServiceProcess, to manage the registration and running of\r
-    '# several services sharing the same process.\r
-    '# NOTE: ServiceHost.Run() and ServiceProcess.Run() are mutually exclusive, that\r
-    '# means don't mix single service with multiple service in the same program!\r
-    type ServiceHost\r
-        '# ctor/dtor()\r
-        declare constructor()\r
-        declare destructor()\r
-        \r
-        '# methods (public)\r
-        declare sub Add(byref as ServiceProcess)\r
-        declare sub Run()\r
-        \r
-        '# properties (public)\r
-        count           as integer\r
-    end type\r
-end namespace   '# fb.svc\r
-end namespace   '# fb\r
-\r
-#ifdef SERVICEFB_INCLUDE_UTILS\r
-#include once "ServiceFB_Utils.bi"\r
-#endif\r
-\r
-#endif '# __ServiceFB_bi__\r
-#endif '# __FB_WIN32__\r
-#endif '# __FB_VERSION__
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bas b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bas
deleted file mode 100644 (file)
index c5150ea..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#include once "ServiceFB.bi"\r
-#include once "_internals.bi"\r
-#include once "ServiceFB_Utils.bi"\r
-#include once "_utils_internals.bi"\r
-\r
-namespace fb\r
-namespace svc\r
-namespace utils   '# fb.svc.utils\r
-    '# private (internals) for ServiceProcess.Console()\r
-    dim shared _svc_stop_signal as any ptr\r
-    dim shared _svc_in_console as ServiceProcess ptr\r
-    dim shared _svc_in_console_stop_flag as BOOL\r
-    \r
-    '#####################\r
-    '# ServiceController\r
-    '# ctor()\r
-    constructor ServiceController()\r
-        with this\r
-            .product = "My Product"\r
-            .version = "v0.1"\r
-            .copyright = "my copyright goes here."\r
-        end with\r
-    end constructor\r
-    \r
-    \r
-    '# ctor(product)\r
-    constructor ServiceController(byref new_product as string)\r
-        this.product = new_product\r
-    end constructor\r
-    \r
-    \r
-    '# ctor(product, version)\r
-    constructor ServiceController(byref new_product as string, byref new_version as string)\r
-        constructor(new_product)\r
-        this.version = new_version\r
-    end constructor\r
-    \r
-    \r
-    '# ctor(product, version, copyright)\r
-    constructor ServiceController(byref new_product as string, byref new_version as string, byref new_copyright as string)\r
-        constructor(new_product, new_version)\r
-        this.copyright = new_copyright\r
-    end constructor\r
-    \r
-    \r
-    '# dtor()\r
-    destructor ServiceController()\r
-    end destructor\r
-    \r
-    \r
-    '# Banner() will display in the console, information regarding your program\r
-    '# using this formatting:\r
-    '# 'Product', 'Version'\r
-    '# 'Copyright'\r
-    sub ServiceController.Banner()\r
-        '# display Product and Version\r
-        print this.product; ", "; this.version\r
-        print this.copyright\r
-        print ""\r
-        '# leave a empty line between banner (header) and other info\r
-    end sub\r
-    \r
-    \r
-    '# RunMode() provide a simple way to get (*you*) from where this process was started\r
-    '# and do the corresponding action.\r
-    function ServiceController.RunMode() as ServiceRunMode\r
-        dim result as ServiceRunMode\r
-        dim currPID as DWORD\r
-        dim parent_pid as uinteger\r
-        dim parent_name as string\r
-        dim start_mode as string\r
-        \r
-        _dprint("ServiceController.RunMode()")\r
-        \r
-        '# get this process PID\r
-        currPID = GetCurrentProcessId()\r
-        _dprint("CurrentPID: " + str(currPID))\r
-        \r
-        '# get the parent PID\r
-        parent_pid = _parent_pid(currPID)\r
-        _dprint("ParentPID: " + str(parent_pid))\r
-        \r
-        '# now the the name\r
-        parent_name = _process_name(parent_pid)\r
-        if (parent_name = "<unknown>") then\r
-          parent_name = _process_name_dyn_psapi(parent_pid)\r
-        end if\r
-        _dprint("Parent Name: " + parent_name)\r
-        \r
-        '# this process started as service?\r
-        '# that means his parent is services.exe\r
-        if (parent_name = "services.exe") then\r
-            result = RunAsService\r
-        else\r
-            '# ok, it didn't start as service, analyze command line then\r
-            start_mode = lcase(trim(command(1)))\r
-            if (start_mode = "manage") then \r
-                '# start ServiceController.Manage()\r
-                result = RunAsManager\r
-            elseif (start_mode = "console") then\r
-                '# start ServiceController.Console()\r
-                result = RunAsConsole\r
-            else\r
-                '# ok, the first paramenter in the commandline didn't work,\r
-                '# report back so we could send the banner!\r
-                result = RunAsUnknown\r
-            end if\r
-        end if\r
-        \r
-        _dprint("ServiceController.RunMode() done")\r
-        return result\r
-    end function\r
-    \r
-    \r
-    '# Manage will offer the user (end-user) option in the commandline to\r
-    '# install, remove, start, stop and query the status of the installed service\r
-    '# use Manage() when you code a multi-services (ServiceHost) based programs\r
-    '# for single services, use Manage(service)\r
-    sub ServiceController.Manage()\r
-    end sub\r
-    \r
-    \r
-    '# this is used when you want management capabilities for your service\r
-    '# use this for single services, or call Manage() for multi services \r
-    sub ServiceController.Manage(byref service as ServiceProcess)\r
-    end sub\r
-    \r
-    \r
-    '# this offer the user a way to test/debug your service or run it like a normal\r
-    '# program, from the command line\r
-    '# will let you SHUTDOWN the service using CTRL+C\r
-    '# use this for multi-services (ServiceHost) based programs\r
-    sub ServiceController.Console()\r
-        dim working_thread as any ptr\r
-        dim run_mode as string\r
-        dim service_name as string\r
-        dim service as ServiceProcess ptr\r
-        dim commandline as string\r
-        dim success as integer\r
-        \r
-        _dprint("ServiceController.Console()")\r
-        \r
-        '# show the controller banner\r
-        this.Banner()\r
-        \r
-        '# determine how many service exist in references\r
-        if (_svc_references_count > 0) then\r
-            _build_commandline(run_mode, service_name, commandline)\r
-            service = _find_in_references(service_name)\r
-            \r
-            if (service = 0) then\r
-                '# no valid service reference, list available services\r
-                _list_references()\r
-            else\r
-                '# build the command line, excluding 'console' and service_name\r
-                service->commandline = commandline\r
-                \r
-                '# got a service reference\r
-                '# also, set the global handler that will be used by _control_handler\r
-                _svc_in_console = service\r
-                \r
-                '# create the signal used to stop the service thread.\r
-                _svc_stop_signal = condcreate()\r
-                \r
-                '# register the Console Handler\r
-                SetConsoleCtrlHandler(@_console_handler, TRUE)\r
-                \r
-                print "Starting service '"; service_name; "' in console mode, please wait..."\r
-                \r
-                '# onInit should be started inline,\r
-                '# and its result validated!\r
-                if not (service->onInit = 0) then\r
-                    success = service->onInit(*service)\r
-                end if\r
-                \r
-                '# only continue if success\r
-                if not (success = 0) then\r
-                    '# now set service.state to running\r
-                    service->state = Running\r
-                    \r
-                    '# now, fire the main loop (onStart)\r
-                    if not (service->onStart = 0) then\r
-                        '# create the thread\r
-                        working_thread = threadcreate(@ServiceProcess.call_onStart, service)\r
-                    end if\r
-                    \r
-                    print "Service is in running state."\r
-                    print "Press Ctrl-C to stop it."\r
-                \r
-                    '# now that onStart is running, must monitor the stop_signal\r
-                    '# in case it arrives, the service state must change to exit the\r
-                    '# working thread.\r
-                    condwait(_svc_stop_signal)\r
-                    \r
-                    print "Stop signal received, stopping..."\r
-                    \r
-                    '# received the signal, so set state = Stopped\r
-                    service->state = Stopped\r
-                    \r
-                    print "Waiting for onStart() to exit..."\r
-                    \r
-                    '# now wait for the thread to terminate\r
-                    if not (working_thread = 0) then\r
-                        threadwait(working_thread)\r
-                    end if\r
-                    \r
-                else\r
-                    print "Error starting the service, onInit() failed."\r
-                end if\r
-                \r
-                print "Service stopped, doing cleanup."\r
-                \r
-                '# remove the console handler\r
-                SetConsoleCtrlHandler(@_console_handler, FALSE)\r
-                \r
-                '# now that service was stopped, destroy the references.\r
-                conddestroy(_svc_stop_signal)\r
-                \r
-                print "Done."\r
-            end if\r
-        else\r
-            print "ERROR: No services could be served by this program. Exiting."\r
-        end if\r
-        \r
-        _dprint("ServiceController.Console() done")\r
-    end sub\r
-    \r
-    \r
-    '# this offer the user a way to test/debug your service or run it like a normal\r
-    '# program, from the command line\r
-    '# will let you SHUTDOWN the service using CTRL+C\r
-    '# use this for single-services\r
-    sub ServiceController.Console(byref service as ServiceProcess)\r
-        \r
-        _dprint("ServiceController.RunMode(service)")\r
-        \r
-        '# register the service in the references table\r
-        _add_to_references(service)\r
-        \r
-        _dprint("delegate to Console()")\r
-        '# now delegate control to Console()\r
-        this.Console()\r
-        \r
-        _dprint("ServiceController.Console(service) done")\r
-    end sub\r
-    \r
-    \r
-    '# console_handler is used to get feedback form keyboard and allow\r
-    '# shutdown of service using Ctrl+C / Ctrl+Break from keyboard\r
-    function _console_handler(byval dwCtrlType as DWORD) as BOOL\r
-        dim result as BOOL\r
-        dim service as ServiceProcess ptr\r
-        \r
-        _dprint("_console_handler()")\r
-        \r
-        '# get the reference from svc_in_console\r
-        service = _svc_in_console\r
-        \r
-        '# we default processing of the message to false\r
-        result = FALSE\r
-        \r
-        '# avoid recursion problems\r
-        if (_svc_in_console_stop_flag = FALSE) then\r
-            _dprint("no previous signaled, process event")\r
-            '# all the CtrlType events listed will raise the onStop\r
-            '# of the service\r
-            '# here also will be raised the _svc_stop_signal\r
-            select case dwCtrlType\r
-                case CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_BREAK_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT:\r
-                    _dprint("got supported CTRL_*_EVENT")\r
-                    '# avoid recursion problems\r
-                    _svc_in_console_stop_flag = TRUE\r
-                    _dprint("set signaled to TRUE")\r
-                    \r
-                    '# the service defined onStop?\r
-                    if not (service->onStop = 0) then\r
-                        _dprint("pass control to onStop()")\r
-                        service->onStop(*service)\r
-                    end if\r
-                    \r
-                    '# now fire the signal\r
-                    _dprint("fire stop signal")\r
-                    condsignal(_svc_stop_signal)\r
-                    result = TRUE\r
-                    _svc_in_console_stop_flag = FALSE\r
-                    \r
-                case else:\r
-                    _dprint("unsupported CTRL EVENT")\r
-                    result = FALSE\r
-            end select\r
-        else\r
-            _dprint("already running onStop(), do not pass the message to other message handlers!")\r
-            result = TRUE\r
-        end if\r
-        \r
-        _dprint("_console_handler() done")\r
-        return result\r
-    end function\r
-    \r
-    \r
-    '# helper private subs used to list the services and their descriptions \r
-    '# in _svc_references\r
-    private sub _list_references()\r
-        dim item as ServiceProcess ptr\r
-        dim idx as integer\r
-        \r
-        print "Available services in this program:"\r
-        \r
-        for idx = 0 to (_svc_references_count - 1)\r
-            item = _svc_references[idx]\r
-            \r
-            print space(2);\r
-            print trim(item->name), , trim(item->description)\r
-        next idx\r
-        \r
-    end sub\r
-    \r
-    \r
-    '# TODO: SimpleLogger\r
-    '# TODO: EventLogger\r
-    \r
-    \r
-    '#####################\r
-    '# private (internals)\r
-    '# _parent_pid is used to retrieve, based on the PID you passed by, the one of the parent\r
-    '# that launched that process.\r
-    '# on fail, it will return 0\r
-    '# Thanks to MichaelW (FreeBASIC forums) for his help about this.\r
-    private function _parent_pid(byval PID as uinteger) as uinteger\r
-        dim as uinteger result\r
-        dim as HANDLE hProcessSnap\r
-        dim as PROCESSENTRY32 pe32\r
-        \r
-        '# initialize result, 0 = fail, other number, ParentPID\r
-        result = 0\r
-        \r
-        hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)\r
-        if not (hProcessSnap = INVALID_HANDLE_VALUE) then\r
-            pe32.dwSize = sizeof(PROCESSENTRY32)\r
-            if (Process32First(hProcessSnap, @pe32) = TRUE) then\r
-                do\r
-                    if (pe32.th32ProcessID = PID) then\r
-                        result = pe32.th32ParentProcessID\r
-                        exit do\r
-                    end if\r
-                loop while not (Process32Next(hProcessSnap, @pe32) = 0)\r
-            end if\r
-        end if\r
-        \r
-        CloseHandle(hProcessSnap)\r
-        return result\r
-    end function\r
-    \r
-    \r
-    '# _process_name is used to retrieve the name (ImageName, BaseModule, whatever) of the PID you\r
-    '# pass to it. if no module name was found, it should return <unknown>\r
-    private function _process_name(byval PID as uinteger) as string\r
-        dim result as string\r
-        dim hProcess as HANDLE\r
-        dim hMod as HMODULE\r
-        dim cbNeeded as DWORD\r
-        \r
-        '# assign "<unknown>" to process name, allocate MAX_PATH (260 bytes)\r
-        result = "<unknown>" \r
-        result += space(MAX_PATH - len(result))\r
-    \r
-        '# get a handle to the Process\r
-        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, PID)\r
-        \r
-        '# if valid, get the process name\r
-        if not (hProcess = NULL) then\r
-            '# success getting Process modules\r
-            if not (EnumProcessModules(hProcess, @hMod, sizeof(hMod), @cbNeeded) = 0) then\r
-                result = space(cbNeeded)\r
-                GetModuleBaseName(hProcess, hMod, strptr(result), len(result))\r
-            end if\r
-        end if\r
-        \r
-        CloseHandle(hProcess)\r
-        \r
-        '# return a trimmed result\r
-        result = trim(result)\r
-        return result\r
-    end function\r
-    \r
-    '# _process_name_dyn_psapi is a workaround for some issues with x64 versions of Windows.\r
-    '# by default, 32bits process can't query information from 64bits modules.\r
-    private function _process_name_dyn_psapi(byval PID as uinteger) as string\r
-        dim result as string\r
-        dim chop as uinteger\r
-        dim zresult as zstring * MAX_PATH\r
-        dim hLib as any ptr\r
-        dim hProcess as HANDLE\r
-        dim cbNeeded as DWORD\r
-        dim GetProcessImageFileName as function (byval as HANDLE, byval as LPTSTR, byval as DWORD) as DWORD\r
-      \r
-        '# assign "<unknown>" to process name, allocate MAX_PATH (260 bytes)\r
-        zresult = "<unknown>" + chr(0)\r
-    \r
-        '# get dynlib\r
-        hLib = dylibload("psapi.dll")\r
-        if not (hlib = 0) then\r
-            GetProcessImageFileName = dylibsymbol(hlib, "GetProcessImageFileNameA")\r
-            if not (GetProcessImageFileName = 0) then\r
-                '# get a handle to the Process\r
-                hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID)\r
-                \r
-                '# if valid, get the process name\r
-                if not (hProcess = NULL) then\r
-                    cbNeeded = sizeof(zresult)\r
-                    if (GetProcessImageFileName(hProcess, @zresult, cbNeeded) = 0) then\r
-                        _dprint("Error with GetProcessImageFileName")\r
-                        _dprint("GetLastError: " + str(GetLastError()) + _show_error())\r
-                    else\r
-                        result = zresult\r
-                        chop = InStrRev(0, result, "\")\r
-                        if (chop > 0) then\r
-                          result = mid(result, chop + 1, (len(result) - chop))\r
-                        end if\r
-                    end if\r
-                else\r
-                    _dprint("Error with OpenProcess")\r
-                    _dprint("GetLastError: " + str(GetLastError()) + _show_error())\r
-                end if\r
-                \r
-                CloseHandle(hProcess)\r
-            else\r
-                _dprint("Unable to get a reference to dynamic symbol GetProcessImageFileNameA.")\r
-            end if\r
-        else\r
-            _dprint("Unable to dynamic load psapi.dll")\r
-        end if\r
-        \r
-        '# return a trimmed result\r
-        'result = trim(result)\r
-        return result\r
-    end function\r
-    \r
-    private function _show_error() as string\r
-        dim buffer as string * 1024\r
-        dim p as integer\r
-        \r
-        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,_\r
-                       0,_\r
-                       GetLastError(),_\r
-                       0,_\r
-                       strptr(buffer),_\r
-                       1024,_\r
-                       0 )\r
-        buffer = rtrim(buffer)\r
-        p = instr(buffer, chr(13))\r
-        if p then buffer = left(buffer, p - 1)\r
-        \r
-        return buffer\r
-    end function\r
-    \r
-    private function InStrRev(byval start as uinteger = 0, byref src as string, byref search as string) as uinteger\r
-        dim lensearch as uinteger = len(search)\r
-        dim as uinteger b, a = 0, exit_loop = 0\r
-        \r
-        do\r
-            b = a\r
-            a += 1\r
-            a = instr(a, src, search)\r
-            if start >= lensearch then if a + lensearch > start then exit_loop = 1\r
-        loop while (a > 0) and (exit_loop = 0)\r
-        \r
-        return b\r
-    end function\r
-\r
-end namespace     '# fb.svc.utils\r
-end namespace     '# fb.svc\r
-end namespace     '# fb\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/ServiceFB_Utils.bi
deleted file mode 100644 (file)
index 8ae1f28..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#if __FB_VERSION__ < "0.17"\r
-#error ServiceFB is designed to compile with FreeBASIC version "0.17"\r
-#else\r
-\r
-#ifndef __FB_WIN32__\r
-#error Platform unsupported. Compiling ServiceFB requires Windows platform.\r
-#else\r
-\r
-#ifndef __ServiceFB_Utils_bi__\r
-#define __ServiceFB_Utils_bi__\r
-\r
-#include once "win/psapi.bi"\r
-#include once "win/tlhelp32.bi"\r
-\r
-namespace fb\r
-namespace svc\r
-namespace utils   '# fb.svc.utils\r
-    '# use this to determine (using select case maybe?) the\r
-    '# mode which the service was invoked.\r
-    enum ServiceRunMode\r
-        RunAsUnknown = 0\r
-        RunAsService\r
-        RunAsManager\r
-        RunAsConsole\r
-    end enum\r
-    \r
-    \r
-    '# ServiceController type (object)\r
-    '# this is a helper object in case you want to implement\r
-    '# console mode (command line testing/debugging) and management (install/remove/control)\r
-    '# to your services, all from the same executable\r
-    type ServiceController\r
-        '# ctor/dtor()\r
-        declare constructor()\r
-        declare constructor(byref as string)\r
-        declare constructor(byref as string, byref as string)\r
-        declare constructor(byref as string, byref as string, byref as string)\r
-        declare destructor()\r
-        \r
-        '# methods (public)\r
-        declare sub Banner()\r
-        declare function RunMode() as ServiceRunMode\r
-        declare sub Manage()\r
-        declare sub Manage(byref as ServiceProcess)\r
-        declare sub Console()\r
-        declare sub Console(byref as ServiceProcess)\r
-        \r
-        '# properties (public)\r
-        '# use these properties for shwoing information on console/manager mode\r
-        '# as banner.\r
-        '# Product, version\r
-        '# copyright\r
-        product     as string\r
-        version     as string\r
-        copyright   as string\r
-    end type\r
-end namespace     '# fb.svc.utils\r
-end namespace     '# fb.svc\r
-end namespace     '# fb\r
-\r
-#endif '# __ServiceFB_bi__\r
-#endif '# __FB_WIN32__\r
-#endif '# __FB_VERSION__
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_internals.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_internals.bi
deleted file mode 100644 (file)
index 55ea882..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-'##################################################################\r
-'#\r
-'# DO NOT INCLUDE THIS FILE DIRECTLY!\r
-'# it is used internaly by ServiceFB\r
-'# use ServiceFB.bi instead\r
-'#\r
-'##################################################################\r
-\r
-namespace fb\r
-namespace svc\r
-    '# now due references locking, I needed a constructor and destructor for \r
-    '# the namespace to garantee everything is cleaned up on termination of the process\r
-    declare sub _initialize() constructor\r
-    declare sub _terminate() destructor\r
-    \r
-    '# global service procedures (private)\r
-    declare sub _main(byval as DWORD, byval as LPSTR ptr)\r
-    declare function _control_ex(byval as DWORD, byval as DWORD, byval as LPVOID, byval as LPVOID) as DWORD\r
-    declare sub _run()\r
-    \r
-    '# global references helper\r
-    declare function _add_to_references(byref as ServiceProcess) as integer\r
-    declare function _find_in_references(byref as string) as ServiceProcess ptr\r
-    \r
-    '# command line builder (helper)\r
-    '# this is used to gather information about:\r
-    '# mode (if present)\r
-    '# valid service name (after lookup in the table)\r
-    '# command line to be passed to service\r
-    declare sub _build_commandline(byref as string, byref as string, byref as string)\r
-    \r
-    '# I started this as simple, unique service served from one process\r
-    '# but the idea of share the same process space (and reduce resources use) was good.\r
-    '# to do that, I needed a references table (similar to service_table, but we will\r
-    '# hold the ServiceProcess registered by ServiceHost (the multi services host).\r
-    '# also, I needed a locking mechanism to avoid problems of two calls changing the table\r
-    '# at the same time.\r
-    extern _svc_references as ServiceProcess ptr ptr\r
-    extern _svc_references_count as integer\r
-    extern _svc_references_lock as any ptr\r
-end namespace   '# fb.svc\r
-end namespace   '# fb\r
-\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_utils_internals.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/ServiceFB/_utils_internals.bi
deleted file mode 100644 (file)
index 67acc87..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-'##################################################################\r
-'#\r
-'# DO NOT INCLUDE THIS FILE DIRECTLY!\r
-'# it is used internaly by ServiceFB\r
-'# use ServiceFB_Utils.bi instead\r
-'#\r
-'##################################################################\r
-\r
-namespace fb\r
-namespace svc\r
-namespace utils   '# fb.svc.utils\r
-    '# console_handler is used to get feedback form keyboard and allow\r
-    '# shutdown of service using Ctrl+C / Ctrl+Break from keyboard\r
-    declare function _console_handler(byval as DWORD) as BOOL\r
-    \r
-    '# helper private subs used to list the services and their descriptions \r
-    '# in _svc_references\r
-    declare sub _list_references()\r
-    \r
-    '# internals functions used to get Parent PID and Process Name\r
-    '# using this we automatically determine if the service was started by SCM\r
-    '# or by the user, from commandline or from explorer\r
-    declare function _parent_pid(byval as uinteger) as uinteger\r
-    declare function _process_name(byval as uinteger) as string\r
-    declare function _process_name_dyn_psapi(byval as uinteger) as string\r
-    declare function _show_error() as string\r
-    \r
-    '# InStrRev (authored by ikkejw @ freebasic forums)\r
-    '# http://www.freebasic.net/forum/viewtopic.php?p=49315#49315\r
-    declare function InStrRev(byval as uinteger = 0, byref as string, byref as string) as uinteger\r
-    \r
-    '# use a signal (condition) in the console mode to know\r
-    '# when the service should be stopped.\r
-    '# the Console() main loop will wait for it, and the console_handler\r
-    '# will raise in case Ctrl+C / Ctrl+Break or other events are\r
-    '# received.\r
-    extern _svc_stop_signal as any ptr\r
-    extern _svc_in_console as ServiceProcess ptr\r
-    extern _svc_in_console_stop_flag as BOOL\r
-end namespace     '# fb.svc.utils\r
-end namespace     '# fb.svc\r
-end namespace     '# fb\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/mongrel_service/init.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/lib/mongrel_service/init.rb
deleted file mode 100644 (file)
index f1475f0..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-require 'gem_plugin'\r
-require 'mongrel'\r
-require 'mongrel/rails'\r
-require 'rbconfig'\r
-require 'fileutils'\r
-\r
-module Service\r
-  class Install < GemPlugin::Plugin "/commands"\r
-    include Mongrel::Command::Base\r
-  \r
-    def configure\r
-        options [\r
-          ['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],\r
-          ['-D', '--display SVC_DISPLAY', "Adjust the display name of the service.", :@svc_display, nil],\r
-          ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],\r
-          ['-p', '--port PORT', "Which port to bind to", :@port, 3000],\r
-          ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],\r
-          ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],\r
-          ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],\r
-          ['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024],\r
-          ['-t', '--timeout TIME', "Timeout all requests after 100th seconds time", :@timeout, 0],\r
-          ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],\r
-          ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],\r
-          ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],\r
-          ['-B', '--debug', "Enable debugging mode", :@debug, false],\r
-          ['-C', '--config PATH', "Use a config file", :@config_file, nil],\r
-          ['-S', '--script PATH', "Load the given file as an extra config script.", :@config_script, nil],\r
-          ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil]\r
-        ]\r
-    end\r
-    \r
-    # When we validate the options, we need to make sure the --root is actually RAILS_ROOT\r
-    # of the rails application we wanted to serve, because later "as service" no error \r
-    # show to trace this.\r
-    def validate\r
-      # TODO: investigate why Win32::Service interfere with gem_plugin\r
-      gem 'win32-service', '>= 0.5.2', '< 0.6.0'\r
-      require 'win32/service'\r
-\r
-      @cwd = File.expand_path(@cwd)\r
-      valid_dir? @cwd, "Invalid path to change to: #@cwd"\r
-  \r
-      # change there to start, then we'll have to come back after daemonize\r
-      Dir.chdir(@cwd)\r
-  \r
-      # start with the premise of app really exist.\r
-      app_exist = true\r
-      %w{app config log}.each do |path|\r
-        if !File.directory?(File.join(@cwd, path))\r
-          app_exist = false\r
-          break\r
-        end\r
-      end\r
-\r
-      valid?(@prefix[0].chr == "/" && @prefix[-1].chr != "/", "Prefix must begin with / and not end in /") if @prefix\r
-\r
-      valid? app_exist == true, "The path you specified isn't a valid Rails application."\r
-\r
-      valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"\r
-      valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"\r
-      valid_dir? @docroot, "Path to docroot not valid: #@docroot"\r
-      valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map\r
-      valid_exists? @config_file, "Config file not there: #@config_file" if @config_file\r
-\r
-      # We should validate service existance here, right Zed?\r
-      begin\r
-        valid? !Win32::Service.exists?(@svc_name), "The service already exist, please remove it first."\r
-      rescue\r
-      end\r
-\r
-      valid? @svc_name != nil, "A service name is mandatory."\r
-      \r
-      # default service display to service name\r
-      @svc_display = @svc_name if !@svc_display\r
-\r
-      return @valid\r
-    end\r
-    \r
-    def run\r
-      gem 'win32-service', '>= 0.5.2', '< 0.6.0'\r
-      require 'win32/service'\r
-\r
-      # check if mongrel_service.exe is in ruby bindir.\r
-      gem_root = File.join(File.dirname(__FILE__), "..", "..")\r
-      gem_executable = File.join(gem_root, "bin/mongrel_service.exe")\r
-      bindir_executable = File.join(Config::CONFIG['bindir'], '/mongrel_service.exe')\r
-      \r
-      unless File.exist?(bindir_executable)\r
-        STDERR.puts "** Copying native mongrel_service executable..."\r
-        FileUtils.cp gem_executable, bindir_executable rescue nil\r
-      end\r
-      \r
-      unless FileUtils.compare_file(bindir_executable, gem_executable)\r
-        STDERR.puts "** Updating native mongrel_service executable..."\r
-        FileUtils.rm_f bindir_executable rescue nil\r
-        FileUtils.cp gem_executable, bindir_executable rescue nil\r
-      end\r
-      \r
-      # build the command line\r
-      argv = []\r
-      \r
-      # start using the native executable\r
-      argv << '"' + bindir_executable + '"'\r
-      \r
-      # use the 'single' service for now\r
-      argv << "single"\r
-      \r
-      # command line setting override config file settings\r
-      @options = { :host => @address,  :port => @port, :cwd => @cwd,\r
-        :log_file => @log_file, :pid_file => @pid_file, :environment => @environment,\r
-        :docroot => @docroot, :mime_map => @mime_map,\r
-        :debug => @debug, :includes => ["mongrel"], :config_script => @config_script,\r
-        :num_procs => @num_procs, :timeout => @timeout, :cpu => @cpu, :prefix => @prefix\r
-      }\r
-      \r
-      # if we are using a config file, pass -c and -C to the service instead of each start parameter.\r
-      if @config_file\r
-        STDERR.puts "** Using #{@config_file} instead of command line parameters."\r
-        conf = YAML.load_file(@config_file)\r
-        \r
-        # add the root folder (-c)\r
-        argv << "-c \"#{conf[:cwd]}\""\r
-        \r
-        # use the config file\r
-        argv << "-C \"#{@config_file}\""\r
-\r
-      else\r
-        # use the command line instead\r
-        # now the options\r
-        argv << "-e #{@options[:environment]}" if @options[:environment]\r
-        argv << "-p #{@options[:port]}"\r
-        argv << "-a #{@options[:host]}"  if @options[:host]\r
-        argv << "-l \"#{@options[:log_file]}\"" if @options[:log_file]\r
-        argv << "-P \"#{@options[:pid_file]}\""\r
-        argv << "-c \"#{@options[:cwd]}\"" if @options[:cwd]\r
-        argv << "-t #{@options[:timeout]}" if @options[:timeout]\r
-        argv << "-m \"#{@options[:mime_map]}\"" if @options[:mime_map]\r
-        argv << "-r \"#{@options[:docroot]}\"" if @options[:docroot]\r
-        argv << "-n #{@options[:num_procs]}" if @options[:num_procs]\r
-        argv << "-B" if @options[:debug]\r
-        argv << "-S \"#{@options[:config_script]}\"" if @options[:config_script]\r
-        argv << "-u #{@options[:cpu.to_i]}" if @options[:cpu]\r
-        argv << "--prefix \"#{@options[:prefix]}\"" if @options[:prefix]\r
-      end\r
-\r
-      svc = Win32::Service.new\r
-      begin\r
-        svc.create_service{ |s|\r
-          s.service_name     = @svc_name\r
-          s.display_name     = @svc_display\r
-          s.binary_path_name = argv.join ' '\r
-          s.dependencies     = []\r
-          s.service_type     = Win32::Service::WIN32_OWN_PROCESS\r
-        }\r
-        puts "Mongrel service '#{@svc_display}' installed as '#{@svc_name}'."\r
-      rescue Win32::ServiceError => err\r
-        puts "There was a problem installing the service:"\r
-        puts err\r
-      end\r
-      svc.close\r
-    end\r
-  end\r
-\r
-  module ServiceValidation\r
-    def configure\r
-      options [\r
-        ['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],\r
-      ]\r
-    end\r
-    \r
-    def validate\r
-      valid? @svc_name != nil, "A service name is mandatory."\r
-\r
-      gem 'win32-service', '>= 0.5.2', '< 0.6.0'\r
-      require 'win32/service'\r
-      \r
-      # Validate that the service exists\r
-      begin\r
-        valid? Win32::Service.exists?(@svc_name), "There is no service with that name, cannot proceed."\r
-        Win32::Service.open(@svc_name) do |svc|\r
-          valid? svc.binary_path_name.include?("mongrel_service"), "The service specified isn't a Mongrel service."\r
-        end\r
-      rescue\r
-      end\r
-      \r
-      return @valid\r
-    end\r
-  end\r
-  \r
-  class Remove < GemPlugin::Plugin "/commands"\r
-    include Mongrel::Command::Base\r
-    include ServiceValidation\r
-    \r
-    def run\r
-      gem 'win32-service', '>= 0.5.2', '< 0.6.0'\r
-      require 'win32/service'\r
-\r
-      display_name = Win32::Service.getdisplayname(@svc_name)\r
-      \r
-      begin\r
-        Win32::Service.stop(@svc_name)\r
-      rescue\r
-      end\r
-      begin\r
-        Win32::Service.delete(@svc_name)\r
-      rescue\r
-      end\r
-      puts "#{display_name} service removed."\r
-    end\r
-  end\r
-end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/mongrel_service.gemspec b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/mongrel_service.gemspec
deleted file mode 100644 (file)
index 95081d0..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-\r
-# Gem::Specification for Mongrel_service-0.3.4\r
-# Originally generated by Echoe\r
-\r
-Gem::Specification.new do |s|\r
-  s.name = %q{mongrel_service}\r
-  s.version = "0.3.4"\r
-  s.date = %q{2008-01-02}\r
-  s.summary = %q{Mongrel Native Win32 Service Plugin for Rails}\r
-  s.email = %q{}\r
-  s.homepage = %q{}\r
-  s.rubyforge_project = %q{mongrel_service}\r
-  s.description = %q{This plugin offer native win32 services for rails, powered by Mongrel.}\r
-  s.has_rdoc = true\r
-  s.platform = %q{i386-mswin32}\r
-  s.authors = ["Luis Lavena"]\r
-  s.files = ["bin/mongrel_service.exe", "tools/freebasic.rb", "TODO", "resources/defaults.yaml", "README", "native/mongrel_service.bi", "native/mongrel_service.bas", "native/console_process.bi", "native/console_process.bas", "native/_debug.bi", "LICENSE", "lib/ServiceFB/ServiceFB_Utils.bi", "lib/ServiceFB/ServiceFB_Utils.bas", "lib/ServiceFB/ServiceFB.bi", "lib/ServiceFB/ServiceFB.bas", "lib/ServiceFB/_utils_internals.bi", "lib/ServiceFB/_internals.bi", "lib/mongrel_service/init.rb", "COPYING", "CHANGELOG", "Manifest", "mongrel_service.gemspec"]\r
-  s.add_dependency(%q<gem_plugin>, [">= 0.2.3", "< 0.3.0"])\r
-  s.add_dependency(%q<mongrel>, [">= 1.0.2", "< 1.2.0"])\r
-  s.add_dependency(%q<win32-service>, [">= 0.5.2", "< 0.6.0"])\r
-end\r
-\r
-\r
-# # Original Rakefile source (requires the Echoe gem):\r
-# \r
-# require 'echoe'\r
-# require 'tools/freebasic'\r
-# \r
-# # Task :package needs compile before doing the gem stuff.\r
-# # (weird behavior of Rake?)\r
-# task :package => [:compile]\r
-# \r
-# echoe_spec = Echoe.new("mongrel_service") do |p|\r
-#   p.summary = "Mongrel Native Win32 Service Plugin for Rails"\r
-#   p.summary += " (debug build)" unless ENV['RELEASE'] \r
-#   p.description = "This plugin offer native win32 services for rails, powered by Mongrel."\r
-#   p.author = "Luis Lavena"\r
-#   p.platform = Gem::Platform::CURRENT\r
-#   p.dependencies = [['gem_plugin', '>=0.2.3', '<0.3.0'],\r
-#                     ['mongrel', '>=1.0.2', '<1.2.0'],\r
-#                     ['win32-service', '>=0.5.2', '<0.6.0']]\r
-# \r
-#   p.executable_pattern = ""\r
-#   \r
-#   p.need_tar_gz = false\r
-#   p.need_zip = true\r
-#   ]\r
-#   p.require_signed = true\r
-# end\r
-# \r
-# desc "Compile native code"\r
-# task :compile => [:native_lib, :native_service]\r
-# \r
-# # global options shared by all the project in this Rakefile\r
-# OPTIONS = {\r
-#   :debug => false,\r
-#   :profile => false,\r
-#   :errorchecking => :ex,\r
-#   :mt => true,\r
-#   :pedantic => true }\r
-# \r
-# OPTIONS[:debug] = true if ENV['DEBUG']\r
-# OPTIONS[:profile] = true if ENV['PROFILE']\r
-# OPTIONS[:errorchecking] = :exx if ENV['EXX']\r
-# OPTIONS[:pedantic] = false if ENV['NOPEDANTIC']\r
-# \r
-# # ServiceFB namespace (lib)\r
-# namespace :lib do\r
-#   project_task 'servicefb' do\r
-#     lib       'ServiceFB'\r
-#     build_to  'lib'\r
-# \r
-#     define    'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE'] \r
-#     source    'lib/ServiceFB/ServiceFB.bas'\r
-#     \r
-#     option    OPTIONS\r
-#   end\r
-#   \r
-#   project_task 'servicefb_utils' do\r
-#     lib       'ServiceFB_Utils'\r
-#     build_to  'lib'\r
-# \r
-#     define    'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE']\r
-#     source    'lib/ServiceFB/ServiceFB_Utils.bas'\r
-#     \r
-#     option    OPTIONS\r
-#   end\r
-# end\r
-# \r
-# # add lib namespace to global tasks\r
-# #include_projects_of :lib\r
-# task :native_lib => "lib:build"\r
-# task :clean => "lib:clobber"\r
-# \r
-# # mongrel_service (native)\r
-# namespace :native do\r
-#   project_task  'mongrel_service' do\r
-#     executable  'mongrel_service'\r
-#     build_to    'bin'\r
-#     \r
-#     define      'DEBUG_LOG' unless ENV['RELEASE']\r
-#     define      "GEM_VERSION=#{echoe_spec.version}"\r
-#     \r
-#     main        'native/mongrel_service.bas'\r
-#     source      'native/console_process.bas'\r
-#     \r
-#     lib_path    'lib'\r
-#     library     'ServiceFB', 'ServiceFB_Utils'\r
-#     library     'user32', 'advapi32', 'psapi'\r
-#     \r
-#     option      OPTIONS\r
-#   end\r
-# end\r
-# \r
-# #include_projects_of :native\r
-# task :native_service => "native:build"\r
-# task :clean => "native:clobber"\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/_debug.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/_debug.bi
deleted file mode 100644 (file)
index 277de2e..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-'##################################################################\r
-'# \r
-'# mongrel_service: Win32 native implementation for mongrel\r
-'#                  (using ServiceFB and FreeBASIC)\r
-'# \r
-'# Copyright (c) 2006 Multimedia systems\r
-'# (c) and code by Luis Lavena\r
-'# \r
-'#  mongrel_service (native) and mongrel_service gem_pluing are licensed\r
-'#  in the same terms as mongrel, please review the mongrel license at\r
-'#  http://mongrel.rubyforge.org/license.html\r
-'#  \r
-'##################################################################\r
-\r
-'##################################################################\r
-'# Requirements:\r
-'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).\r
-'# \r
-'##################################################################\r
-\r
-#ifndef __Debug_bi__\r
-#define __Debug_bi__\r
-\r
-#ifdef DEBUG_LOG\r
-    #include once "vbcompat.bi"\r
-    #ifndef DEBUG_LOG_FILE\r
-        #define DEBUG_LOG_FILE EXEPATH + "\debug.log"\r
-    #endif\r
-\r
-    '# this procedure is only used for debugging purposed, will be removed from\r
-    '# final compilation\r
-    private sub debug_to_file(byref message as string, byref file as string, byval linenumber as uinteger, byref func as string)\r
-        dim handle as integer\r
-        static first_time as integer\r
-        \r
-        handle = freefile\r
-        open DEBUG_LOG_FILE for append as #handle\r
-        \r
-        if (first_time = 0) then\r
-            print #handle, "# Logfile created on "; format(now(), "dd/mm/yyyy HH:mm:ss")\r
-            print #handle, ""\r
-            first_time = 1\r
-        end if\r
-        \r
-        '# src/module.bas:123, namespace.function:\r
-        '#   message\r
-        '#\r
-        print #handle, file; ":"; str(linenumber); ", "; lcase(func); ":"\r
-        print #handle, space(2); message\r
-        print #handle, ""\r
-        \r
-        close #handle\r
-    end sub\r
-    #define debug(message) debug_to_file(message, __FILE__, __LINE__, __FUNCTION__)\r
-#else\r
-    #define debug(message)\r
-#endif '# DEBUG_LOG\r
-\r
-#endif '# __Debug_bi__\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bas b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bas
deleted file mode 100644 (file)
index 78dc1a0..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-'#--\r
-'# Copyright (c) 2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#include once "console_process.bi"\r
-\r
-constructor ConsoleProcess(byref new_filename as string = "", byref new_arguments as string = "")\r
-    '# assign filename and arguments\r
-    \r
-    '# if filename contains spaces, automatically quote it!\r
-    if (instr(new_filename, " ") > 0) then\r
-        _filename = !"\"" + new_filename + !"\""\r
-    else\r
-        _filename = new_filename\r
-    endif\r
-    \r
-    _arguments = new_arguments\r
-end constructor\r
-\r
-destructor ConsoleProcess()\r
-    '# in case process still running\r
-    if (running = true) then\r
-        terminate(true)\r
-        \r
-        '# close opened handles\r
-        '# ...\r
-        CloseHandle(_process_info.hProcess)\r
-        CloseHandle(_process_info.hThread)\r
-    end if\r
-end destructor\r
-\r
-property ConsoleProcess.filename() as string\r
-    return _filename\r
-end property\r
-\r
-property ConsoleProcess.filename(byref rhs as string)\r
-    if not (running = true) then\r
-        _filename = rhs\r
-    end if\r
-end property\r
-\r
-property ConsoleProcess.arguments() as string\r
-    return _arguments\r
-end property\r
-\r
-property ConsoleProcess.arguments(byref rhs as string)\r
-    if not (running = true) then\r
-        _arguments = rhs\r
-    end if\r
-end property\r
-\r
-property ConsoleProcess.redirected_stdout() as string\r
-    return _stdout_filename\r
-end property\r
-\r
-property ConsoleProcess.redirected_stderr() as string\r
-    return _stderr_filename\r
-end property\r
-\r
-'# running is a helper which evaluates _pid and exit_code\r
-property ConsoleProcess.running() as boolean\r
-    dim result as boolean\r
-\r
-    '# presume not running\r
-    result = false\r
-    \r
-    if not (_pid = 0) then\r
-        '# that means the process is/was running.\r
-        '# now evaluate if exit_code = STILL_ACTIVE\r
-        result = (exit_code = STILL_ACTIVE)\r
-    end if\r
-    \r
-    return result\r
-end property\r
-\r
-property ConsoleProcess.pid() as uinteger\r
-    return _pid\r
-end property\r
-\r
-property ConsoleProcess.exit_code() as uinteger\r
-    dim result as uinteger\r
-    \r
-    result = 0\r
-    \r
-    '# is _pid valid?\r
-    if not (_pid = 0) then\r
-        if not (_process_info.hProcess = NULL) then\r
-            '# the process reference is valid, get the exit_code\r
-            if not (GetExitCodeProcess(_process_info.hProcess, @result) = 0) then\r
-                '# OK\r
-                '# no error in the query, get result\r
-            end if '# not (GetExitCodeProcess() = 0)\r
-        end if '# not (proc = NULL)\r
-    end if '# not (_pid = 0)\r
-    \r
-    return result\r
-end property\r
-\r
-function ConsoleProcess.redirect(byval target as ProcessStdEnum, byref new_std_filename as string) as boolean\r
-    dim result as boolean\r
-    \r
-    if not (running = true) then\r
-        select case target\r
-            case ProcessStdOut:\r
-                _stdout_filename = new_std_filename\r
-                result = true\r
-                \r
-            case ProcessStdErr:\r
-                _stderr_filename = new_std_filename\r
-                result = true\r
-                \r
-            case ProcessStdBoth:\r
-                _stdout_filename = new_std_filename\r
-                _stderr_filename = new_std_filename\r
-                result = true\r
-        \r
-        end select\r
-    end if\r
-    \r
-    return result\r
-end function\r
-\r
-function ConsoleProcess.start() as boolean\r
-    dim result as boolean\r
-    dim success as boolean\r
-    \r
-    '# API\r
-    '# New Process resources\r
-    dim context as STARTUPINFO\r
-    dim proc_sa as SECURITY_ATTRIBUTES = type(sizeof(SECURITY_ATTRIBUTES), NULL, TRUE)\r
-    \r
-    '# StdIn, StdOut, StdErr Read and Write Pipes.\r
-    dim as HANDLE StdInRd, StdOutRd, StdErrRd\r
-    dim as HANDLE StdInWr, StdOutWr, StdErrWr\r
-    dim merged as boolean\r
-    \r
-    '# cmdline\r
-    dim cmdline as string\r
-    \r
-    '# assume start will fail\r
-    result = false\r
-    \r
-    if (running = false) then\r
-        '# we should create the std* for the new proc!\r
-        '# (like good parents, prepare everything!)\r
-        \r
-        '# to ensure everything will work, we must allocate a console\r
-        '# using AllocConsole, even if it fails.\r
-        '# This solve the problems when running as service.\r
-        '# we discard result of AllocConsole since we ALWAYS will allocate it.\r
-        AllocConsole()\r
-        \r
-        '# assume all the following steps succeed\r
-        success = true\r
-        \r
-        '# StdIn is the only std that will be created using pipes always\r
-        '# StdIn\r
-        if (CreatePipe(@StdInRd, @StdInWr, @proc_sa, 0) = 0) then \r
-            success = false\r
-        end if\r
-        \r
-        '# Ensure the handles to the pipe are not inherited.\r
-        if (SetHandleInformation(StdInWr, HANDLE_FLAG_INHERIT, 0) = 0) then\r
-            success = false\r
-        end if\r
-        \r
-        '# StdOut and StdErr should be redirected?\r
-        if (not _stdout_filename = "") or _\r
-            (not _stderr_filename = "") then\r
-            \r
-            '# out and err are the same? (merged)\r
-            if (_stdout_filename = _stderr_filename) then\r
-                merged = true\r
-            end if\r
-        end if\r
-        \r
-        '# StdOut if stdout_filename\r
-        if not (_stdout_filename = "") then\r
-            StdOutWr = CreateFile(strptr(_stdout_filename), _\r
-                                    GENERIC_WRITE, _\r
-                                    FILE_SHARE_READ or FILE_SHARE_WRITE, _\r
-                                    @proc_sa, _\r
-                                    OPEN_ALWAYS, _\r
-                                    FILE_ATTRIBUTE_NORMAL, _\r
-                                    NULL)\r
-            \r
-            if (StdOutWr = INVALID_HANDLE_VALUE) then\r
-                '# failed to open file\r
-                success = false\r
-            else\r
-                SetFilePointer(StdOutWr, 0, NULL, FILE_END)\r
-            end if\r
-        else\r
-            '# use pipes instead\r
-            '# StdOut\r
-            if (CreatePipe(@StdOutRd, @StdOutWr, @proc_sa, 0) = 0) then \r
-                success = false\r
-            end if\r
-            \r
-            if (SetHandleInformation(StdOutRd, HANDLE_FLAG_INHERIT, 0) = 0) then\r
-                success = false\r
-            end if\r
-        end if 'not (_stdout_filename = "")\r
-        \r
-        '# only create stderr if no merged.\r
-        if (merged = true) then\r
-            StdErrWr = StdOutWr\r
-        else\r
-            '# do the same for StdErr...\r
-            if not (_stderr_filename = "") then\r
-                StdErrWr = CreateFile(strptr(_stderr_filename), _\r
-                                        GENERIC_WRITE, _\r
-                                        FILE_SHARE_READ or FILE_SHARE_WRITE, _\r
-                                        @proc_sa, _\r
-                                        OPEN_ALWAYS, _\r
-                                        FILE_ATTRIBUTE_NORMAL, _\r
-                                        NULL)\r
-                \r
-                if (StdErrWr = INVALID_HANDLE_VALUE) then\r
-                    '# failed to open file\r
-                    success = false\r
-                else\r
-                    SetFilePointer(StdErrWr, 0, NULL, FILE_END)\r
-                end if\r
-            else\r
-                '# use pipes instead\r
-                '# StdOut\r
-                if (CreatePipe(@StdErrRd, @StdErrWr, @proc_sa, 0) = 0) then \r
-                    success = false\r
-                end if\r
-                \r
-                if (SetHandleInformation(StdErrRd, HANDLE_FLAG_INHERIT, 0) = 0) then\r
-                    success = false\r
-                end if\r
-                \r
-            end if 'not (_stderr_filename = "")\r
-        end if '(merged = true)\r
-        \r
-        '# now we must proceed to create the process\r
-        '# without the pipes, we shouldn't continue!\r
-        if (success = true) then\r
-            '# Set the Std* handles ;-)\r
-            with context\r
-                .cb = sizeof( context )\r
-                .hStdError = StdErrWr\r
-                .hStdOutput = StdOutWr\r
-                .hStdInput = StdInRd\r
-                .dwFlags = STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW\r
-                '# FIXME: .wShowWindow = iif((_show_console = true), SW_SHOW, SW_HIDE)\r
-                .wShowWindow = SW_HIDE\r
-            end with\r
-            \r
-            '# build the command line\r
-            cmdline = _filename + " " + _arguments\r
-            \r
-            '# now creates the process\r
-            if (CreateProcess(NULL, _\r
-                                strptr(cmdline), _\r
-                                NULL, _\r
-                                NULL, _\r
-                                1, _            '# win32 TRUE (1)\r
-                                0, _\r
-                                NULL, _\r
-                                NULL, _\r
-                                @context, _\r
-                                @_process_info) = 0) then\r
-                result = false\r
-            else\r
-                '# set the _pid\r
-                _pid = _process_info.dwProcessId\r
-                \r
-                '# OK? yeah, I think so.\r
-                result = true\r
-                \r
-                '# close the Std* handles\r
-                CloseHandle(StdInRd)\r
-                CloseHandle(StdInWr)\r
-                CloseHandle(StdOutRd)\r
-                CloseHandle(StdOutWr)\r
-                CloseHandle(StdErrRd)\r
-                CloseHandle(StdErrWr)\r
-                \r
-                '# close children main Thread handle\r
-                'CloseHandle(proc.hThread)\r
-                'CloseHandle(proc.hProcess)\r
-                \r
-            end if '# (CreateProcess() = 0)\r
-        else\r
-            result = false\r
-        end if '# (success = TRUE)\r
-    end if\r
-    \r
-    return result\r
-end function\r
-\r
-function ConsoleProcess.terminate(byval force as boolean = false) as boolean\r
-    dim result as boolean\r
-    dim success as boolean\r
-    \r
-    dim proc as HANDLE\r
-    dim code as uinteger\r
-    dim wait_code as uinteger\r
-    \r
-    '# is pid valid?\r
-    if (running = true) then\r
-        '# hook our custom console handler\r
-        if not (SetConsoleCtrlHandler(@_console_handler, 1) = 0) then\r
-            success = true\r
-        end if\r
-        \r
-        if (success = true) then\r
-            '# get a handle to Process\r
-            proc = _process_info.hProcess\r
-            if not (proc = NULL) then\r
-                '# process is valid, perform actions\r
-                success = false\r
-                \r
-                if not (force = true) then\r
-                    '# send CTRL_C_EVENT and wait for result\r
-                    if not (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0) = 0) then\r
-                        '# it worked, wait 5 seconds terminates.\r
-                        wait_code = WaitForSingleObject(proc, 5000)\r
-                        if not (wait_code = WAIT_TIMEOUT) then\r
-                            success = true\r
-                        end if\r
-                    else\r
-                        success = false\r
-                    end if\r
-                    \r
-                    '# Ctrl-C didn't work, try Ctrl-Break\r
-                    if (success = false) then\r
-                        '# send CTRL_BREAK_EVENT and wait for result\r
-                        if not (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0) = 0) then\r
-                            '# it worked, wait 5 seconds terminates.\r
-                            wait_code = WaitForSingleObject(proc, 5000) \r
-                            if not (wait_code = WAIT_TIMEOUT) then\r
-                                success = true\r
-                            end if\r
-                        else\r
-                            success = false\r
-                        end if\r
-                    end if\r
-                    \r
-                '# only do termination if force was set.\r
-                elseif (force = true) and (success = false) then\r
-                    '# still no luck? we should do a hard kill then\r
-                    if (TerminateProcess(proc, 0) = 0) then\r
-                        success = false\r
-                    else\r
-                        success = true\r
-                    end if\r
-                end if\r
-                \r
-                '# now get process exit code\r
-                if (success = true) then\r
-                    result = true\r
-                else\r
-                    result = false\r
-                end if\r
-            else\r
-                '# invalid process handler\r
-                result = false\r
-            end if\r
-            \r
-        end if '# (success = true)\r
-        \r
-        '# remove hooks\r
-        if not (SetConsoleCtrlHandler(@_console_handler, 0) = 0) then\r
-            success = true\r
-        end if\r
-    end if '# not (pid = 0)\r
-    \r
-    return result\r
-end function\r
-\r
-function ConsoleProcess._console_handler(byval dwCtrlType as DWORD) as BOOL\r
-    dim result as BOOL\r
-    \r
-    if (dwCtrlType = CTRL_C_EVENT) then\r
-        result = 1\r
-    elseif (dwCtrlType = CTRL_BREAK_EVENT) then\r
-        result = 1\r
-    end if\r
-    \r
-    return result\r
-end function\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/console_process.bi
deleted file mode 100644 (file)
index 3ad49ad..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-'#--\r
-'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems\r
-'#\r
-'# This source code is released under the MIT License.\r
-'# See MIT-LICENSE file for details\r
-'#++\r
-\r
-#ifndef __CONSOLE_PROCESS_BI__\r
-#define __CONSOLE_PROCESS_BI__\r
-\r
-#include once "windows.bi"\r
-#include once "boolean.bi"\r
-\r
-enum ProcessStdEnum\r
-    ProcessStdOut   = 1 \r
-    ProcessStdErr   = 2\r
-    ProcessStdBoth  = 3\r
-end enum\r
-\r
-type ConsoleProcess\r
-    '# this class provide basic functionality\r
-    '# to control child processes\r
-    \r
-    '# new ConsoleProcess(Filename, Parameters)\r
-    declare constructor(byref as string = "", byref as string = "")\r
-    \r
-    '# delete\r
-    declare destructor()\r
-    \r
-    '# properties (only getters)\r
-    declare property filename as string\r
-    declare property filename(byref as string)\r
-    \r
-    declare property arguments as string\r
-    declare property arguments(byref as string)\r
-    \r
-    '# stdout and stderr allow you redirect\r
-    '# console output and errors to files\r
-    declare property redirected_stdout as string\r
-    declare property redirected_stderr as string\r
-    \r
-    '# evaluate if the process is running\r
-    declare property running as boolean\r
-    \r
-    '# pid will return the current Process ID, or 0 if no process is running\r
-    declare property pid as uinteger\r
-    \r
-    '# exit_code is the value set by the process prior exiting.\r
-    declare property exit_code as uinteger\r
-    \r
-    '# methods\r
-    declare function redirect(byval as ProcessStdEnum, byref as string) as boolean\r
-    declare function start() as boolean\r
-    declare function terminate(byval as boolean = false) as boolean\r
-    \r
-    private:\r
-        _filename as string\r
-        _arguments as string\r
-        _pid as uinteger\r
-        _process_info as PROCESS_INFORMATION\r
-        _show_console as boolean = false\r
-        \r
-        _redirect_stdout as boolean\r
-        _stdout_filename as string\r
-        \r
-        _redirect_stderr as boolean\r
-        _stderr_filename as string\r
-        \r
-        '# this fake console handler\r
-        '# is used to trap ctrl-c\r
-        declare static function _console_handler(byval as DWORD) as BOOL\r
-        \r
-end type 'ConsoleProcess\r
-\r
-#endif '__CONSOLE_PROCESS_BI__\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bas b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bas
deleted file mode 100644 (file)
index 49caa1b..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-'##################################################################\r
-'# \r
-'# mongrel_service: Win32 native implementation for mongrel\r
-'#                  (using ServiceFB and FreeBASIC)\r
-'# \r
-'# Copyright (c) 2006 Multimedia systems\r
-'# (c) and code by Luis Lavena\r
-'# \r
-'#  mongrel_service (native) and mongrel_service gem_pluing are licensed\r
-'#  in the same terms as mongrel, please review the mongrel license at\r
-'#  http://mongrel.rubyforge.org/license.html\r
-'#  \r
-'##################################################################\r
-\r
-'##################################################################\r
-'# Requirements:\r
-'# - FreeBASIC 0.18\r
-'# \r
-'##################################################################\r
-\r
-#include once "mongrel_service.bi"\r
-#define DEBUG_LOG_FILE EXEPATH + "\mongrel_service.log"\r
-#include once "_debug.bi"\r
-\r
-namespace mongrel_service\r
-    constructor SingleMongrel()\r
-        dim redirect_file as string\r
-        \r
-        with this.__service\r
-            .name = "single"\r
-            .description = "Mongrel Single Process service"\r
-            \r
-            '# disabling shared process\r
-            .shared_process = FALSE\r
-            \r
-            '# TODO: fix inheritance here\r
-            .onInit = @single_onInit\r
-            .onStart = @single_onStart\r
-            .onStop = @single_onStop\r
-        end with\r
-        \r
-        with this.__console\r
-            redirect_file = EXEPATH + "\mongrel.log"\r
-            debug("redirecting to: " + redirect_file)\r
-            .redirect(ProcessStdBoth, redirect_file)\r
-        end with\r
-        \r
-        '# TODO: fix inheritance here\r
-        single_mongrel_ref = @this\r
-    end constructor\r
-    \r
-    destructor SingleMongrel()\r
-        '# TODO: fin inheritance here\r
-    end destructor\r
-    \r
-    function single_onInit(byref self as ServiceProcess) as integer\r
-        dim result as integer\r
-        dim mongrel_cmd as string\r
-        \r
-        debug("single_onInit()")\r
-        \r
-        '# ruby.exe must be in the path, which we guess is already there.\r
-        '# because mongrel_service executable (.exe) is located in the same\r
-        '# folder than mongrel_rails ruby script, we complete the path with\r
-        '# EXEPATH + "\mongrel_rails" to make it work.\r
-        '# FIXED ruby installation outside PATH and inside folders with spaces\r
-        mongrel_cmd = !"\"" + EXEPATH + !"\\ruby.exe" + !"\" " + !"\"" + EXEPATH + !"\\mongrel_rails" + !"\"" + " start"\r
-        \r
-        '# due lack of inheritance, we use single_mongrel_ref as pointer to \r
-        '# SingleMongrel instance. now we should call StillAlive\r
-        self.StillAlive()\r
-        if (len(self.commandline) > 0) then\r
-            '# assign the program\r
-            single_mongrel_ref->__console.filename = mongrel_cmd\r
-            single_mongrel_ref->__console.arguments = self.commandline\r
-            \r
-            '# fix commandline, it currently contains params to be passed to\r
-            '# mongrel_rails, and not ruby.exe nor the script to be run.\r
-            self.commandline = mongrel_cmd + " " + self.commandline\r
-            \r
-            '# now launch the child process\r
-            debug("starting child process with cmdline: " + self.commandline)\r
-            single_mongrel_ref->__child_pid = 0\r
-            if (single_mongrel_ref->__console.start() = true) then\r
-                single_mongrel_ref->__child_pid = single_mongrel_ref->__console.pid\r
-            end if\r
-            self.StillAlive()\r
-            \r
-            '# check if pid is valid\r
-            if (single_mongrel_ref->__child_pid > 0) then\r
-                '# it worked\r
-                debug("child process pid: " + str(single_mongrel_ref->__child_pid))\r
-                result = not FALSE\r
-            end if\r
-        else\r
-            '# if no param, no service!\r
-            debug("no parameters was passed to this service!")\r
-            result = FALSE\r
-        end if\r
-        \r
-        debug("single_onInit() done")\r
-        return result\r
-    end function\r
-    \r
-    sub single_onStart(byref self as ServiceProcess)\r
-        debug("single_onStart()")\r
-        \r
-        do while (self.state = Running) or (self.state = Paused)\r
-            '# instead of sitting idle here, we must monitor the pid\r
-            '# and re-spawn a new process if needed\r
-            if not (single_mongrel_ref->__console.running = true) then\r
-                '# check if we aren't terminating\r
-                if (self.state = Running) or (self.state = Paused) then\r
-                    debug("child process terminated!, re-spawning a new one")\r
-                    \r
-                    single_mongrel_ref->__child_pid = 0\r
-                    if (single_mongrel_ref->__console.start() = true) then\r
-                        single_mongrel_ref->__child_pid = single_mongrel_ref->__console.pid\r
-                    end if\r
-                    \r
-                    if (single_mongrel_ref->__child_pid > 0) then\r
-                        debug("new child process pid: " + str(single_mongrel_ref->__child_pid))\r
-                    end if\r
-                end if\r
-            end if\r
-            \r
-            '# wait for 5 seconds\r
-            sleep 5000\r
-        loop\r
-        \r
-        debug("single_onStart() done")\r
-    end sub\r
-    \r
-    sub single_onStop(byref self as ServiceProcess)\r
-        debug("single_onStop()")\r
-        \r
-        '# now terminates the child process\r
-        if not (single_mongrel_ref->__child_pid = 0) then\r
-            debug("trying to kill pid: " + str(single_mongrel_ref->__child_pid))\r
-            if not (single_mongrel_ref->__console.terminate() = true) then\r
-                debug("Terminate() reported a problem when terminating process " + str(single_mongrel_ref->__child_pid))\r
-            else\r
-                debug("child process terminated with success.")\r
-                single_mongrel_ref->__child_pid = 0\r
-            end if\r
-        end if\r
-        \r
-        debug("single_onStop() done")\r
-    end sub\r
-    \r
-    sub application()\r
-        dim simple as SingleMongrel\r
-        dim host as ServiceHost\r
-        dim ctrl as ServiceController = ServiceController("Mongrel Win32 Service", "version " + VERSION, _\r
-                                                            "(c) 2006 The Mongrel development team.")\r
-        \r
-        '# add SingleMongrel (service)\r
-        host.Add(simple.__service)\r
-        select case ctrl.RunMode()\r
-            '# call from Service Control Manager (SCM)\r
-            case RunAsService:\r
-                debug("ServiceHost RunAsService")\r
-                host.Run()\r
-                \r
-            '# call from console, useful for debug purposes.\r
-            case RunAsConsole:\r
-                debug("ServiceController Console")\r
-                ctrl.Console()\r
-                \r
-            case else:\r
-                ctrl.Banner()\r
-                print "mongrel_service is not designed to run form commandline,"\r
-                print "please use mongrel_rails service:: commands to create a win32 service."\r
-        end select\r
-    end sub\r
-end namespace\r
-\r
-'# MAIN: start native mongrel_service here\r
-mongrel_service.application()\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bi b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/native/mongrel_service.bi
deleted file mode 100644 (file)
index d5ea0dc..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-'##################################################################\r
-'# \r
-'# mongrel_service: Win32 native implementation for mongrel\r
-'#                  (using ServiceFB and FreeBASIC)\r
-'# \r
-'# Copyright (c) 2006 Multimedia systems\r
-'# (c) and code by Luis Lavena\r
-'# \r
-'#  mongrel_service (native) and mongrel_service gem_pluing are licensed\r
-'#  in the same terms as mongrel, please review the mongrel license at\r
-'#  http://mongrel.rubyforge.org/license.html\r
-'#  \r
-'##################################################################\r
-\r
-'##################################################################\r
-'# Requirements:\r
-'# - FreeBASIC 0.18.\r
-'# \r
-'##################################################################\r
-\r
-#define SERVICEFB_INCLUDE_UTILS\r
-#include once "lib/ServiceFB/ServiceFB.bi"\r
-#include once "console_process.bi"\r
-\r
-'# use for debug versions\r
-#if not defined(GEM_VERSION)\r
-  #define GEM_VERSION (debug mode)\r
-#endif\r
-\r
-'# preprocessor stringize\r
-#define PPSTR(x) #x\r
-\r
-namespace mongrel_service\r
-    const VERSION as string = PPSTR(GEM_VERSION)\r
-    \r
-    '# namespace include\r
-    using fb.svc\r
-    using fb.svc.utils\r
-    \r
-    declare function single_onInit(byref as ServiceProcess) as integer\r
-    declare sub single_onStart(byref as ServiceProcess)\r
-    declare sub single_onStop(byref as ServiceProcess)\r
-    \r
-    '# SingleMongrel\r
-    type SingleMongrel\r
-        declare constructor()\r
-        declare destructor()\r
-        \r
-        '# TODO: replace for inheritance here\r
-        'declare function onInit() as integer\r
-        'declare sub onStart()\r
-        'declare sub onStop()\r
-        \r
-        __service       as ServiceProcess\r
-        __console       as ConsoleProcess\r
-        __child_pid     as uinteger\r
-    end type\r
-    \r
-    '# TODO: replace with inheritance here\r
-    dim shared single_mongrel_ref as SingleMongrel ptr\r
-end namespace\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/resources/defaults.yaml b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/resources/defaults.yaml
deleted file mode 100644 (file)
index 4a05eb1..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
---- \r
-:debug: false\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/tools/freebasic.rb b/ruby/lib/ruby/gems/1.8/gems/mongrel_service-0.3.4-x86-mswin32/tools/freebasic.rb
deleted file mode 100644 (file)
index 8902059..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-#--\r
-# FreeBASIC project builder\r
-# inspired by Asset Compiler by Jeremy Voorhis\r
-# coded by Luis Lavena\r
-#--\r
-\r
-# FreeBASIC Project library for compilation.\r
-# For example:\r
-#\r
-# namespace :projects do\r
-#   project_task :my_fb_project do\r
-#     lib         'static'\r
-#     dylib       'dynamic library'\r
-#     executable  'exename'\r
-#     \r
-#     build_to    'bin'\r
-#     \r
-#     define      'MY_DEFINE'\r
-#     \r
-#     main        'src/main.bas'\r
-#     source      'src/other_module.bas'\r
-#     source      "src/*.bas"\r
-#   end\r
-# end\r
-#\r
-# This example defines the following tasks:\r
-#\r
-#   rake projects:build                 # Build all projects\r
-#   rake projects:clobber               # Clobber all projects\r
-#   rake projects:my_fb_project:build   # Build the my_fb_project files\r
-#   rake projects:my_fb_project:clobber # Remove the my_fb_project files\r
-#   rake projects:my_fb_project:rebuild # Force a rebuild of the my_fb_project files\r
-#   rake projects:rebuild               # Rebuild all projects\r
-\r
-require 'rake/tasklib'\r
-require 'pp'\r
-\r
-module FreeBASIC\r
-  # this help me reduce the attempts to remove already removed files.\r
-  # works with src_files\r
-  CLOBBER = Rake::FileList.new\r
-  ON_WINDOWS = (RUBY_PLATFORM =~ /mswin|cygwin|bccwin/)\r
-  \r
-  class ProjectTask\r
-    attr_accessor :name\r
-    attr_accessor :output_name\r
-    attr_accessor :type\r
-    attr_accessor :build_path\r
-    attr_accessor :defines\r
-    attr_accessor :main_file\r
-    attr_accessor :sources\r
-    attr_accessor :libraries\r
-    attr_accessor :search_path\r
-    attr_accessor :libraries_path\r
-    \r
-    def initialize(name, &block)\r
-      @name = name.to_s\r
-      @build_path = '.'\r
-      @defines = []\r
-      @sources = Rake::FileList.new\r
-      @libraries = []\r
-      @search_path = []\r
-      @libraries_path = []\r
-      @options = {}\r
-      \r
-      instance_eval &block\r
-      \r
-      do_cleanup\r
-      \r
-      namespace @name do\r
-        define_clobber_task\r
-        define_rebuild_task\r
-        define_build_directory_task\r
-        define_build_task\r
-      end\r
-      add_dependencies_to_main_task\r
-    end \r
-    \r
-    public\r
-      # using this will set your project type to :executable\r
-      # and assign exe_name as the output_name for the project\r
-      # it dynamically will assign extension when running on windows\r
-      def executable(exe_name)\r
-        @type = :executable\r
-        @output_name = "#{exe_name}.#{(ON_WINDOWS ? "exe" : "")}"\r
-        @real_file_name = @output_name\r
-      end\r
-      \r
-      # lib will set type to :lib and assign 'lib#{lib_name}.a'\r
-      # as output_name for the project\r
-      def lib(lib_name)\r
-        @type = :lib\r
-        @output_name = lib_name\r
-        @real_file_name = "lib#{lib_name}.a"\r
-      end\r
-      \r
-      # dynlib will set type to :dynlib and assign '#{dll_name}.dll|so'\r
-      # as output_name for this project\r
-      def dylib(dll_name)\r
-        @type = :dylib\r
-        @output_name = "#{dll_name}.#{(ON_WINDOWS ? "dll" : "so")}"\r
-        @real_file_name = @output_name\r
-        @complement_file = "lib#{@output_name}.a"\r
-      end\r
-      \r
-      # this set where the final, compiled executable should be linked\r
-      # uses @build_path as variable\r
-      def build_to(path)\r
-        @build_path = path\r
-      end\r
-      \r
-      # define allow you set compiler time options\r
-      # collect them into @defines and use later\r
-      # to call source file compilation process (it wil require cleanup)\r
-      def define(*defines)\r
-        @defines << defines\r
-      end\r
-      \r
-      # main set @main_file to be the main module used during the linking\r
-      # process. also, this module requires special -m flag during his\r
-      # own compile process\r
-      # question: in case @no main_file was set, will use the first 'source'\r
-      # file defined as main? or it should raise a error?\r
-      def main(main_file)\r
-        @main_file = main_file\r
-      end\r
-      \r
-      # used to collect sources files for compilation\r
-      # it will take .bas, .rc, .res as sources\r
-      # before compilation we should clean @sources!\r
-      def source(*sources)\r
-        @sources.include sources\r
-      end\r
-    \r
-      # this is similar to sources, instead library linking is used\r
-      # also will require cleanup services ;-)\r
-      def library(*libraries)\r
-        @libraries << libraries\r
-      end\r
-  \r
-      # use this to set the include path when looking for, ehem, includes\r
-      # (equals -i fbc compiler param)\r
-      def search_path(*search_path)\r
-        @search_path << search_path\r
-      end \r
-      \r
-      # use this to tell the compiler where to look for libraries\r
-      # (equals -p fbc compiler param)\r
-      def lib_path(*libraries_path)\r
-        @libraries_path << libraries_path\r
-      end\r
-      \r
-      # use this to add additional compiler parameters (like debug or errorchecking options)\r
-      #\r
-      def option(new_options = {})\r
-        @options.merge!(new_options)\r
-      end\r
-      \r
-    protected\r
-      # this method will fix nested libraries and defines\r
-      # also, if main_file is missing (or wasn't set) will shift the first one\r
-      # as main\r
-      def do_cleanup\r
-        # remove duplicated definitions, flatten \r
-        @defines.flatten!\r
-        @defines.uniq! if @defines.length > 1\r
-\r
-        # set main_file\r
-        if @type == :executable\r
-          @main_file = @sources.shift unless defined?(@main_file)\r
-        end\r
-        \r
-        # empty path? must be corrected\r
-        @build_path = '.' if @build_path == ''\r
-        \r
-        # remove duplicates from sources\r
-        @sources.uniq! if @sources.length > 1\r
-        \r
-        # now the libraries\r
-        @libraries.flatten!\r
-        @libraries.uniq! if @libraries.length > 1\r
-        \r
-        # search path\r
-        @search_path.flatten!\r
-        @search_path.uniq! if @search_path.length > 1\r
-        \r
-        # libraries path\r
-        @libraries_path.flatten!\r
-        @libraries_path.uniq! if @libraries_path.length > 1\r
-        \r
-        # if no target was set, default to executable\r
-        unless defined?(@output_name)\r
-          executable(@name)\r
-        end\r
-      end\r
-      \r
-      # return the compiled name version of the passed source file (src)\r
-      # compiled_form("test.bas") => "test.o"\r
-      def compiled_form(src)\r
-        src.ext({ ".bas" => "o", ".rc" => "obj" }[File.extname(src)])\r
-      end\r
-      \r
-      def compiled_project_file\r
-        File.join @build_path, @real_file_name\r
-      end\r
-      \r
-      def fbc_compile(source, target, main = nil)\r
-        cmdline = []\r
-        cmdline << "fbc"\r
-        cmdline << "-g" if (@options.has_key?(:debug) && @options[:debug] == true)\r
-        cmdline << "-#{@options[:errorchecking].to_s}" if @options.has_key?(:errorchecking)\r
-        cmdline << "-profile" if (@options.has_key?(:profile) && @options[:profile] == true)\r
-        cmdline << "-mt" if (@options.has_key?(:mt) && @options[:mt] == true)\r
-        cmdline << "-w pedantic" if (@options.has_key?(:pedantic) && @options[:pedantic] == true)\r
-        cmdline << "-c #{source}"\r
-        cmdline << "-o #{target}"\r
-        cmdline << "-m #{main}" unless main.nil?\r
-        cmdline << @defines.collect { |defname| "-d #{defname}" }\r
-        cmdline << @search_path.collect { |path| "-i #{path}" }\r
-        cmdline.flatten.join(' ')\r
-      end\r
-      \r
-      def fbc_link(target, files, extra_files = [])\r
-        cmdline = []\r
-        cmdline << "fbc"\r
-        cmdline << "-g" if (@options.has_key?(:debug) && @options[:debug] == true)\r
-        cmdline << "-profile" if (@options.has_key?(:profile) && @options[:profile] == true)\r
-        cmdline << "-mt" if (@options.has_key?(:mt) && @options[:mt] == true)\r
-        cmdline << "-#{@type.to_s}" unless @type == :executable\r
-        cmdline << "-x #{target}"\r
-        cmdline << files << extra_files\r
-        cmdline << @defines.collect { |defname| "-d #{defname}" }\r
-        unless @type == :lib\r
-          cmdline << @libraries_path.collect { |path| "-p #{path}" }\r
-          cmdline << @libraries.collect { |libname| "-l #{libname}" }\r
-        end\r
-        cmdline.flatten.join(' ')\r
-      end\r
-      \r
-      def define_clobber_task\r
-        desc "Remove all compiled files for #{@name}"\r
-        task :clobber do\r
-          # remove compiled and linked file\r
-          rm compiled_project_file rescue nil #unless @type == :lib\r
-          rm File.join(@build_path, @complement_file) rescue nil if @type == :dylib\r
-          \r
-          # remove main file\r
-          rm compiled_form(@main_file) rescue nil\r
-          \r
-          # now the sources files\r
-          # avoid attempt to remove the file two times (this is a bug in Rake)\r
-          @sources.each do |src|\r
-            # exclude compiled source files (c obj).\r
-            unless src =~ /o$/\r
-              target = compiled_form(src)\r
-              unless CLOBBER.include?(target)\r
-                CLOBBER.include(target)\r
-                rm target rescue nil\r
-              end\r
-            end\r
-          end\r
-        end\r
-      end\r
-      \r
-      def define_rebuild_task\r
-        desc "Force a rebuild of files for #{@name}"\r
-        task :rebuild => [:clobber, :build]\r
-      end\r
-      \r
-      def define_build_directory_task\r
-        directory @build_path\r
-        task :build => @build_path\r
-      end\r
-      \r
-      def define_build_task\r
-        desc "Build project #{@name}"\r
-        task :build\r
-        \r
-        # empty file task\r
-        file compiled_project_file\r
-        \r
-        # compile main_file\r
-        # use as pre-requisite the source filename\r
-        if @type == :executable\r
-          file compiled_form(@main_file) => @main_file do |t|\r
-            # remove the path and the extension\r
-            main_module = File.basename(t.name).ext\r
-            sh fbc_compile(@main_file, t.name, main_module)\r
-          end\r
-        \r
-          # add dependency\r
-          file compiled_project_file => compiled_form(@main_file)\r
-        end\r
-        \r
-        # gather files that are passed "as-is" to the compiler\r
-        unprocessed_files = @sources.select { |rcfile| rcfile =~ /(res|rc|o|obj)$/ }\r
-        \r
-        @sources.each do |src|\r
-          # is a unprocessed file?\r
-          unless unprocessed_files.include?(src)\r
-            target = compiled_form(src)\r
-            \r
-            # is already in our list of tasks?\r
-            if not Rake::Task.task_defined?(target)\r
-              # if not, compile\r
-              \r
-              file target => src do\r
-                sh fbc_compile(src, target)\r
-              end\r
-            end\r
-          \r
-            # include dependency\r
-            file compiled_project_file => target\r
-          end\r
-        end\r
-        \r
-        # now the linking process\r
-        file compiled_project_file do |t|\r
-          target = File.join(@build_path, @output_name)\r
-          sh fbc_link(target, t.prerequisites, unprocessed_files)\r
-        end\r
-        \r
-        # add the dependency\r
-        task :build => compiled_project_file\r
-      end\r
-\r
-      # Adds dependencies in the parent namespace\r
-      def add_dependencies_to_main_task\r
-        desc 'Build all projects' unless task( :build ).comment\r
-        task :build => "#{@name}:build"\r
-        \r
-        desc 'Clobber all projects' unless task( :clobber ).comment\r
-        task :clobber => "#{@name}:clobber"\r
-        \r
-        desc 'Rebuild all projects' unless task( :rebuild ).comment\r
-        task :rebuild => ["#{@name}:clobber", "#{@name}:build"]\r
-      end\r
-  end\r
-end\r
-  \r
-# helper method to define a FreeBASIC::ProjectTask in the current namespace\r
-def project_task name, &block\r
-  FreeBASIC::ProjectTask.new name, &block\r
-end\r
-\r
-def include_projects_of name\r
-  desc 'Build all projects' unless task( :build ).comment\r
-  task :build => "#{name}:build"\r
-  \r
-  desc 'Clobber all projects' unless task( :clobber ).comment\r
-  task :clobber => "#{name}:clobber"\r
-  \r
-  desc 'Rebuild all projects' unless task( :rebuild ).comment\r
-  task :rebuild => "#{name}:rebuild"\r
-end\r
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/CHANGELOG b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/CHANGELOG
new file mode 100644 (file)
index 0000000..592018d
--- /dev/null
@@ -0,0 +1,316 @@
+== 1.2.11 Bat-Shit Crazy
+ * Fix pure Ruby gem to not include binary.
+
+== 1.2.10 I'm dumb (BAD RELEASE, DON'T USE)
+ * I really am (bad release fix)
+
+== 1.2.9 Black Keys Extra Plus Wow (BAD RELEASE, DON'T USE)
+ * Improve fat binary loading.
+
+== 1.2.8 Black Keys
+ * Allow the connection to remain open for 1xx statuses [timshadel]
+
+     Both the 100 and 101 status codes require that the connection to the
+     server remain open. The 100 status code is used to tell the client that
+     the server is still receiving its request, and will continue to read
+     request input on the connection. The 101 status code is used to upgrade
+     the existing connection to another protocol, and specifically is NOT
+     used to upgrade a separate connection. Therefore, the connection must
+     remain open after this response in order to facilitate that.
+   
+ * Accept IE7 badly encoded URL (eg.: %uEEEE)
+ * Fix gemspec to work w/ Bundler [smparkes]
+ * Add SSL support [tmm1]
+ * Catch Errno::EPERM in Process.running? [Tony Kemp]
+   On some systems (e.g. OpenBSD) you receive an EPERM exception if
+   you try to Process.getpgid on a process you do not own (even if you
+   are root). But it does mean that the process exists, so return true.
+ * Fix Rails version check that select which Rack adapter to use. Was using CGI adapter in Rails 3.
+ * Ignore SIGHUP when no restart block is given
+ * Add SSL options to thin command line tool [goldmann]
+
+      --ssl                    Enables SSL
+      --ssl-key-file PATH      Path to private key
+      --ssl-cert-file PATH     Path to certificate
+      --ssl-verify             Enables SSL certificate verification
+    
+  * Expose peer SSL certificate in env (rack.peer_cert) [fd]
+  * Adjusting unix socket permissions to be more open [mbj]
+  
+== 1.2.7 No Hup
+ * Support multiple Ruby version (fat binaries under windows)
+ * Do not trap unsupported HUP signal on Windows
+
+== 1.2.6 Crazy Delicious
+ * Make work with Rails 3 out-of-the-box.
+ * Auto-detect and load config.ru files on start. Makes Rails 3 work.
+ * Fix signals being ignored under 1.9 when daemonized.
+
+== 1.2.5 This Is Not A Web Server
+ * Add rolling restart support (--onebyone option) [sikachu]
+ * Force external_encoding of request's body to ASCII_8BIT [jeremyz]
+ * Ensure Rack base API is used in Rails adapter only if version >= 2.3.2 [#111 state:resolved]
+
+== 1.2.4 Flaming Astroboy
+ * Fix a few issues in thin to make it a better "gem citizen" [josh]
+ * Fix test for rack based Rails in adapter under Ruby >= 1.8.7 [#109 state:resolved]
+ * Fix Remote address spoofing vulnerability in Connection#remote_address [Alexey Borzenkov]
+ * Fix uninitialized constant ActionController::Dispatcher error with Rails 1.2.3 [Chris Anderton] [#103 state:resolved]
+
+== 1.2.2 I Find Your Lack of Sauce Disturbing release
+ * Fix force kill under 1.9 [Alexey Chebotar]
+ * Fix regression when --only option is used w/ --socket.
+ * Add process name 'tag' functionality. Easier to distinguish thin daemons 
+   from eachother in process listing [ctcherry]
+
+== 1.2.1 Asynctilicious Ultra Supreme release
+ * Require Rack 1.0.0
+ * Require EventMachine 0.12.6
+ * Use Rails Rack based dispatcher when available
+ * Allow String for response body
+ * Require openssl before eventmachine to prevent crash in 1.9
+
+== 1.2.0 Asynctilicious Supreme release 
+ * Add support for Windows mingw Ruby distro [Juan C. Rodriguez]
+ * Add async response support, see example/async_*.ru [raggi]
+
+== 1.1.1 Super Disco Power Plus release 
+ * Fix bug when running with only options [hasimo]
+
+== 1.1.0 Super Disco Power release
+ * Require EventMachine 0.12.4
+ * Remove Thin handler, now part of Rack 0.9.1
+ * Fix Rack protocol version to 0.1 in environment hash.
+ * Fix error when passing no_epoll option to a cluster.
+ * Omit parsing #defined strings [Jérémy Zurcher]
+ * Defaults SERVER_NAME to localhost like webrick does [#87 state:resolved]
+ * Namespace parser to prevent error when mongrel is required [cliffmoon]
+ * Set RACK_ENV based on environment option when loading rackup file [Curtis Summers] [#83 state:resolved]
+ * Fixes a warning RE relative_url_root when using a prefix with Rails 2.1.1 [seriph] [#85 state:resolved]
+ * --only can work as a sequence number (if < 80) or a port number (if >= 80) [jmay] [#81 state:resolved]
+
+== 1.0.0 That's What She Said release
+ * Fixed vlad.rake to allow TCP or socket [hellekin]
+ * Updated Mack adapter to handle both <0.8.0 and >0.8.0 [Mark Bates]
+ * rails rack adapter uses File.readable_real? so it recognizes ACL permissions [Ricardo Chimal]
+ * Log a warning if Rack application returns nil body [Michael S. Klishin]
+ * Handle nil and Time header values correctly [#76 state:resolved] [tmm1]
+ * Add Content-Length header to response automatically when possible [#74 state:resolved] [dkubb]
+ * Runner now remembers -r, -D and -V parameters so that clustered servers inherit those and
+   `restart` keep your parameters.
+ * Make Set-Cookie header, in Rails adapter, compatible with current Rack spec [Pedro Belo]
+   [#73, state:resolved]
+ * Add --no-epoll option to disable epoll usage on Linux [#61 state:resolved]
+ * Add --force (-f) option to force stopping of a daemonized server [#72 state:resolved]
+ * Update halycon adapter loader [mtodd]
+
+== 0.8.2 Double Margarita release
+ * Require EventMachine 0.12.0
+ * [bug] Fix timeout handling when running command
+ * [bug] Fix hanging when restarting and no process is running in single server move, fixes #67
+ * Added Mack adapter [markbates]
+ * Allow rackup .rb files by getting a conventionally named constant as the app [bmizerany]
+
+== 0.8.1 Rebel Porpoise release
+ * [bug] Rescue all types of errors when processing request, fixes #62
+ * [bug] Use Swiftiply backend when -y option is specified, fixes #63 and #64
+ * Allow passing port as a string in Server.new
+ * Define deferred?(env) in your Rack application to set if a request is handled in a
+        thread (return true) or not (return false).
+
+== 0.8.0 Dodgy Dentist release
+ * [bug] Fix server crash when header too large.
+ * Add --require (-r) option to require a library, before executing your script.
+ * Rename --rackup short option to -R, warn and load as rackup when file ends with .ru.
+ * List supported adapters in command usage.
+ * Add file adapter to built-in adapter, serve static files in current directory.
+ * Allow disabling signal handling in Server with :signals => false
+ * Make Server.new arguments more flexible, can now specify any of host, port, app or hash options.
+ * Add --backend option to specified which backend to use, closes #55
+ * [bug] Serve static file only on GET and HEAD requests in Rails adapter, fixes #58
+ * Add threaded option to run server in threaded mode, calling the application in a
+   thread allowing for concurrency in the Rack adapter, closes #46
+ * Guess which adapter to use from directory (chdir option)
+   or use specified one in 'adapter' option, re #47.
+
+== 0.7.1 Fancy Pants release
+ * Clean stale PID files when starting as daemon, fixes #53 [Chu Yeow]
+ * Require EventMachine 0.11.0 for UNIX domain sockets. Until it's released, install from:
+   gem install eventmachine --source http://code.macournoyer.com
+ * Ruby 1.8.5 compatibility, closes #49 [Wincent Colaiuta]
+ * Move all EventMachine stuff out of Server, you can now create a Thin Backend that
+   does not depend on EventMachine.
+ * Rename Connector to Backend. Extend Thin::Backends::Base to implement your own.
+ * Fix high memory usage with big POST body, fixes #48
+
+== 0.7.0 Spherical Cow release
+ * Add --max-persistent-conns option to sets the maximum number of persistent connections.
+   Set to 0 to disable Keep-Alive.
+ * INT signal now force stop and QUIT signal gracefully stops.
+ * Warn when descriptors table size can't be set as high as expected.
+ * Eval Rackup config file using top level bindings.
+ * Remove daemons gem dependency on Windows plateform, fixes #45.
+ * Change default timeout from 60 to 30 seconds.
+ * Add --max-conns option to sets the maximum number of file or socket descriptors that
+   your process may open, defaults to 1024.
+ * Tail logfile when stopping and restarting a demonized server, fixes #26.
+ * Wrap application in a Rack::CommonLogger adapter in debug mode.
+ * --debug (-D) option no longer set $DEBUG so logging will be less verbose
+   and Ruby won't be too strict, fixes #36.
+ * Deprecate Server#silent in favour of Logging.silent.
+ * Persistent connection (keep-alive) support.
+ * Fix -s option not being included in generated config file, fixes #37.
+ * Add Swiftiply support. Use w/ the --swiftiply (-y) option in the thin script,
+   closes #28 [Alex MacCaw]
+
+== 0.6.4 Sexy Lobster release
+ * Fix error when stopping server on UNIX domain socket, fixes #42
+ * Rescue errors in Connection#get_peername more gracefully, setting REMOTE_ADDR to nil, fixes #43
+
+== 0.6.3 Ninja Cookie release
+ * Add tasks for Vlad the Deployer in example/vlad.rake [cnantais]
+ * Add Ramaze Rackup config file in example dir [tmm1]
+   Use like this from you Ramaze app dir:
+   
+     thin start -r /path/to/thin/example/ramaze.ru
+   
+ * Add the --rackup option to load a Rack config file instead of the Rails adapter.
+   So you can use any framework with the thin script and start cluster and stuff like that.
+   A Rack config file is one that is usable through the rackup command and looks like this:
+     
+     use Rack::CommonLogger
+     run MyCrazyRackAdapter.new(:uterly, 'cool')
+     
+   Then use it with thin like this:
+     
+     thin start --rackup config.ru
+     
+ * thin config --chrdir ... -C thin/yml do not change current directory anymore, fixes #33.
+ * Add a better sample god config file in example/thin.god that loads all info from config
+   files in /etc/thin. Drop-in replacement for the thin runlevel service [Gump].
+ * Add support for specifying a custom Connector to the server and add more doc about Server
+   configuration.
+ * Add a script to run thin as a runlevel service that can start at startup, closes #31 [Gump]
+   Setup the service like this:
+   
+     sudo thin install /etc/thin
+   
+   This will install the boot script under /etc/init.d/thin. Then copy your config files to
+   /etc/thin. Works only under Linux.
+ * Set process name to 'thin server (0.0.0.0:3000)' when running as a daemon, closes #32.
+ * Make sure chdir option from config file is used when present.
+ * Raise an error when starting a server as a daemon and pid file already exist, fixes #27.
+
+== 0.6.2 Rambo release
+ * Server now let current connections finish before stopping, fixes #18
+ * Fix uploading hanging bug when body is moved to a tempfile,
+   also delete the tempfile properly upon completion, fixes #25
+ * 'thin restart' now sends HUP signals rather then stopping & starting, closes #17
+ * HUP signal now launches a new process with the same options.
+ * Add PID and more info from the last request to the Stats adapter
+   mostly taken from Rack::ShowException.
+ * pid and log files in cluster are no longer required to be relative to the
+   app directory (chdir option), fixes #24
+ * Revert to using #each when building response headers under Ruby 1.8,
+   solves an issue w/ Camping adapter, fixes #22
+ * Restructure thin script options in 3 sections: server, daemon and cluster
+ * Add --only (-o) option to control only one server of a cluster.
+ * Stylize stats page and make the url configurable from the thin script.
+ * Raise error if attempting to use unix sockets on windows.
+ * Add example config files for http://www.tildeslash.com/monit usage.
+   Include the example file using "include /path/to/thin/monit/file" in your monitrc file.
+   The group settings let you do this to manage your clusters:
+   
+     sudo monit -g blog restart all
+    
+   There are examples of thin listening on sockets and thin listening on unix sockets.
+
+== 0.6.1 Cheesecake release
+ * Remove socket file when server stops.
+ * Set back cluster to use 'thin' command to launch servers.
+
+== 0.6.0 Big Pony release
+ * Add support for connection through UNIX domain socket.
+   Use the --socket (-S) option w/ the thin script to configure the socket filename.
+   Nginx support binding to a UNIX socket like this:
+   
+     upstream  backend {
+       server   unix:/tmp/thin.0.sock;
+       server   unix:/tmp/thin.1.sock;
+       server   unix:/tmp/thin.2.sock;
+     }
+  
+   Start your servers like this:
+   
+     thin start -s3 -S/tmp/thin.sock
+   
+ * Remove Server#listen! method. Use Server#start instead.
+ * Server can now yield a Rack::Builder to allow building an app in one call:
+     Server.start '0.0.0.0', 3000 do
+       use Rack::CommonLogger
+       use Rack::ShowExceptions
+       map "/lobster" do
+         use Rack::Lint
+         run Rack::Lobster.new
+       end
+     end
+     
+ * Add a very basic stats page through Stats adapter, load w/ --stats and browse to /stats.
+ * Add --trace (-V) option to trace request/response and get backtrace w/out all Ruby debug stuff.
+ * Add --config (-C) option to load options from a config file in thin script [Matt Todd].
+ * Alter response headers to output directly to a string.
+ * Improve specs stability.
+ * Move request body to a Tempfile if too big (> 112 MB)
+ * Remove useless check for max header size in Request (already done in the parser)
+
+== 0.5.4 Flying Mustard release
+ * Don't read the full body, use direct streaming when sending response.
+   See: Response#each
+   As a result, the Content-Length can not be calculated anymore.
+   You have to do set this in your adapter. All frameworks do it anyway.
+   It improve memory usage and boost speed for low concurrency.
+   Thanks to Kent Sibilev and Ezra for their help on that one.
+ * Add 'Server' response header
+ * Fix --user and --group option not changing daemon process privileges
+== 0.5.3 Purple Yogurt release
+ * win32 pre-compiled gem now available
+ * change rake task configuration to allow win32 gem build
+ * Add prefix option to thin script to mount app under a given path.
+
+== 0.5.2 Cheezburger release
+ * Add cluster support through the -s option in the thin script, start 3 thins like this:
+    thin start -s3 -p3000
+   3 thin servers will be started on port 3000, 3001, 3002, also the port number will be
+   injected in the pid and log filenames.
+ * Fix IOError when writing to logger when starting server as a daemon.
+ * Really change directory when the -c option is specified.
+ * Add restart command to thin script.
+ * Fix typos in thin script usage message and expand chdir path.
+ * Rename thin script options to be the same as mongrel_rails script [thronedrk]:
+     -o --host  => -a --address
+     --log-file => --log
+     --pid-file => --pid
+     --env      => --environment
+== 0.5.1 LOLCAT release
+ * Add URL rewriting to Rails adapter so that page caching works and / fetches index.html if present.
+ * Fix bug in multiline response header parsing.
+ * Add specs for the Rails adapter.
+ * Fix Set-Cookie headers in Rails adapter to handle multiple values correctly.
+ * Fix Ruby 1.9 incompatibility in Response#headers= and Rakefile.
+ * Fix parser to be Ruby 1.9 compatible [Aman Gupta]
+ * Set gemspec to use EventMachine version 0.8.1 as it's the latest one having precompiled windows binaries.
+   [Francis Cianfrocca].
+ * Add -D option to thin script to set debugging on.
+ * Output incoming data and response when debugging is on.
+
+== 0.5.0
+ * Full rewrite to use EventMachine, Rack and Mongrel parser
+== 0.4.1
+ * Fix Rails environment option not being used in thin script.
+== 0.4.0
+ * First alphaish release as a gem.
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/COPYING b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/COPYING
new file mode 100644 (file)
index 0000000..19d3d7c
--- /dev/null
@@ -0,0 +1,18 @@
+Copyright (c) 2008 Marc-Andre Cournoyer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+  
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+   
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/README b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/README
new file mode 100644 (file)
index 0000000..6f1ec06
--- /dev/null
@@ -0,0 +1,69 @@
+== Thin
+Tiny, fast & funny HTTP server
+
+Thin is a Ruby web server that glues together 3 of the best Ruby libraries in web history:
+* the Mongrel parser: the root of Mongrel speed and security
+* Event Machine: a network I/O library with extremely high scalability, performance and stability
+* Rack: a minimal interface between webservers and Ruby frameworks
+
+Which makes it, with all humility, the most secure, stable, fast and extensible Ruby web server
+bundled in an easy to use gem for your own pleasure.
+
+Site:  http://code.macournoyer.com/thin/
+Group: http://groups.google.com/group/thin-ruby/topics
+Bugs:  http://thin.lighthouseapp.com/projects/7212-thin
+Code:  http://github.com/macournoyer/thin
+IRC:   #thin on freenode
+
+=== Installation
+For the latest stable version:
+
+ sudo gem install thin
+
+Or from source:
+
+ git clone git://github.com/macournoyer/thin.git
+ cd thin
+ rake install
+
+=== Usage
+A +thin+ script offers an easy way to start your Rails application:
+
+ cd to/your/rails/app
+ thin start
+
+But Thin is also usable with a Rack config file.
+You need to setup a config.ru file and pass it to the thin script:
+  
+ cat config.ru
+ app = proc do |env|
+  [
+    200,
+    {
+      'Content-Type' => 'text/html',
+      'Content-Length' => '2'
+    },
+    ['hi']
+  ]
+ end
+ run app
+
+ thin start -R config.ru
+See example directory for more samples and run 'thin -h' for usage.
+
+=== License
+Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
+
+=== Credits
+The parser was stolen from Mongrel http://mongrel.rubyforge.org by Zed Shaw.
+Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
+<zedshaw at zedshaw dot com> You can redistribute it and/or modify it under
+either the terms of the GPL.
+
+Thin is copyright Marc-Andre Cournoyer <macournoyer@gmail.com>
+
+Get help at http://groups.google.com/group/thin-ruby/
+Report bugs at http://thin.lighthouseapp.com/projects/7212-thin
+and major security issues directly to a team member (see COMMITTERS)
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/Rakefile b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/Rakefile
new file mode 100644 (file)
index 0000000..26e8628
--- /dev/null
@@ -0,0 +1,44 @@
+RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
+WIN      = (RUBY_PLATFORM =~ /mswin|cygwin/)
+SUDO     = (WIN ? "" : "sudo")
+
+require 'rake'
+require 'rake/clean'
+require 'rake/extensiontask' # from rake-compiler gem
+
+$: << File.join(File.dirname(__FILE__), 'lib')
+require 'thin/version'
+
+# Load tasks in tasks/
+Dir['tasks/**/*.rake'].each { |rake| load rake }
+
+task :default => :spec
+
+Rake::ExtensionTask.new('thin_parser', Thin::GemSpec) do |ext|
+  # enable cross compilation (requires cross compile toolchain)
+  ext.cross_compile = true
+  
+  # forces the Windows platform instead of the default one
+  # configure options only for cross compile
+  ext.cross_platform = %w( i386-mswin32 x86-mingw32 )
+end
+
+CLEAN.include %w(**/*.{o,bundle,jar,so,obj,pdb,lib,def,exp,log} ext/*/Makefile ext/*/conftest.dSYM lib/1.{8,9}})
+
+desc "Compile the Ragel state machines"
+task :ragel do
+  Dir.chdir 'ext/thin_parser' do
+    target = "parser.c"
+    File.unlink target if File.exist? target
+    sh "ragel parser.rl -G2 -o #{target}"
+    raise "Failed to compile Ragel state machine" unless File.exist? target
+  end
+end
+
+desc "Build gem packages"
+task :gems do
+  sh "rake clean gem && rake cross native gem RUBY_CC_VERSION=1.8.6:1.9.1"
+end
+
+desc "Release version #{Thin::VERSION::STRING} gems to rubyforge"
+task :release => [:tag, "gem:push"]
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/abc b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/abc
new file mode 100644 (file)
index 0000000..67dfa9f
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env ruby
+# Automate benchmarking with ab with various concurrency levels.
+require 'optparse'
+
+options = {
+  :address  => '0.0.0.0',
+  :port     => 3000,
+  :requests => 1000,
+  :start    => 1,
+  :end      => 100,
+  :step     => 10
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
+
+  opts.on("-n", "--requests NUM", "Number of requests")         { |num| options[:requests] = num }
+  opts.on("-a", "--address HOST", "Address (default: 0.0.0.0)") { |host| options[:address] = host }
+  opts.on("-p", "--port PORT", "use PORT (default: 3000)")      { |port| options[:port] = port.to_i }
+  opts.on("-s", "--start N", "First concurrency level")         { |n| options[:start] = n.to_i }
+  opts.on("-e", "--end N", "Last concurrency level")            { |n| options[:end] = n.to_i }
+  opts.on("-S", "--step N", "Concurrency level step")           { |n| options[:step] = n.to_i }
+  opts.on("-u", "--uri PATH", "Path to send to")                { |u| options[:uri] = u }
+  opts.on("-k", "--keep-alive", "Use Keep-Alive")               { options[:keep_alive] = true }
+  
+  opts.on_tail("-h", "--help", "Show this message")     { puts opts; exit }
+end.parse!(ARGV)
+
+puts 'request   concurrency   req/s    failures'
+puts '=' * 42
+
+c = options[:start]
+until c >= options[:end]
+  sleep 0.5
+  out = `nice -n20 ab #{'-k' if options[:keep_alive]} -c #{c} -n #{options[:requests]} #{options[:address]}:#{options[:port]}/#{options[:uri]} 2> /dev/null`
+
+  r = if requests = out.match(/^Requests.+?(\d+\.\d+)/)
+    requests[1].to_i
+  else
+    0
+  end
+  f = if requests = out.match(/^Failed requests.+?(\d+)/)
+    requests[1].to_i
+  else
+    0
+  end
+  
+  puts "#{options[:requests].to_s.ljust(9)} #{c.to_s.ljust(13)} #{r.to_s.ljust(8)} #{f}"
+  
+  c += options[:step]
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/benchmarker.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/benchmarker.rb
new file mode 100644 (file)
index 0000000..1737824
--- /dev/null
@@ -0,0 +1,80 @@
+require 'rack/lobster'
+
+class Benchmarker
+  PORT    = 7000
+  ADDRESS = '0.0.0.0'
+  
+  attr_accessor :requests, :concurrencies, :servers, :keep_alive
+  
+  def initialize
+    @servers = %w(Mongrel EMongrel Thin)
+    @requests = 1000
+    @concurrencies = [1, 10, 100]
+  end
+  
+  def writer(&block)
+    @writer = block
+  end
+  
+  def run!
+    @concurrencies.each do |concurrency|
+      @servers.each do |server|
+        req_sec, failed = run_one(server, concurrency)
+        @writer.call(server, @requests, concurrency, req_sec, failed)
+      end
+    end
+  end
+  
+  private
+    def start_server(handler_name)
+      @server = fork do
+        [STDOUT, STDERR].each { |o| o.reopen "/dev/null" }
+
+        case handler_name
+        when 'EMongrel'
+          require 'swiftcore/evented_mongrel'
+          handler_name = 'Mongrel'
+        end
+
+        app = proc do |env|
+          [200, {'Content-Type' => 'text/html', 'Content-Length' => '11'}, ['hello world']]
+        end
+
+        handler = Rack::Handler.const_get(handler_name)
+        handler.run app, :Host => ADDRESS, :Port => PORT
+      end
+    
+      sleep 2
+    end
+  
+    def stop_server
+      Process.kill('SIGKILL', @server)
+      Process.wait
+    end
+  
+    def run_ab(concurrency)
+      `nice -n20 ab -c #{concurrency} -n #{@requests} #{@keep_alive ? '-k' : ''} #{ADDRESS}:#{PORT}/ 2> /dev/null`
+    end
+  
+    def run_one(handler_name, concurrency)
+      start_server(handler_name)
+
+      out = run_ab(concurrency)
+
+      stop_server
+
+      req_sec = if matches = out.match(/^Requests.+?(\d+\.\d+)/)
+        matches[1].to_i
+      else
+        0
+      end
+    
+      failed = if matches = out.match(/^Failed requests.+?(\d+)/)
+        matches[1].to_i
+      else
+        0
+      end
+    
+      [req_sec, failed]
+    end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/runner b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/benchmark/runner
new file mode 100644 (file)
index 0000000..b870765
--- /dev/null
@@ -0,0 +1,82 @@
+#!/usr/bin/env ruby
+# Simple benchmark to compare Thin performance against
+# other webservers supported by Rack.
+#
+# Run with:
+#
+#  ruby simple.rb [num of request] [print|graph] [concurrency levels]
+#
+
+$: << File.join(File.dirname(__FILE__), '..', 'lib')
+require 'thin'
+
+require File.dirname(__FILE__) + '/benchmarker'
+require 'optparse'
+
+options = {
+  :requests      => 1000,
+  :concurrencies => [1, 10, 100],
+  :keep_alive    => false,
+  :output        => :table
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
+
+  opts.on("-n", "--requests NUM", "Number of requests")         { |num| options[:requests] = num.to_i }
+  opts.on("-c", "--concurrencies EXP", "Concurrency levels")    { |exp| options[:concurrencies] = eval(exp).to_a }
+  opts.on("-k", "--keep-alive", "Use persistent connections")   { options[:keep_alive] = true }
+  opts.on("-t", "--table", "Output as text table")              { options[:output] = :table }
+  opts.on("-g", "--graph", "Output as graph")                   { options[:output] = :graph }
+  
+  opts.on_tail("-h", "--help", "Show this message")     { puts opts; exit }
+end.parse!(ARGV)
+
+# benchmark output_type, %w(WEBrick Mongrel EMongrel Thin), request, levels
+b = Benchmarker.new
+b.requests = options[:requests]
+b.concurrencies = options[:concurrencies]
+b.keep_alive = options[:keep_alive]
+
+case options[:output]
+when :table
+  puts 'server     request   concurrency   req/s    failures'
+  puts '=' * 52
+  
+  b.writer do |server, requests, concurrency, req_sec, failed|
+    puts "#{server.ljust(8)}   #{requests}      #{concurrency.to_s.ljust(4)}          #{req_sec.to_s.ljust(8)} #{failed}"
+  end
+  
+  b.run!
+
+when :graph
+  require '/usr/local/lib/ruby/gems/1.8/gems/gruff-0.2.9/lib/gruff'
+  g = Gruff::Area.new
+  g.title = "#{options[:requests]} requests"
+  g.title << ' w/ Keep-Alive' if options[:keep_alive]
+  
+  g.x_axis_label  = 'Concurrency'
+  g.y_axis_label  = 'Requests / sec'
+  g.maximum_value = 0
+  g.minimum_value = 0
+  g.labels        = {}
+  b.concurrencies.each_with_index { |c, i| g.labels[i] = c.to_s }
+  
+  results = {}
+  
+  b.writer do |server, requests, concurrency, req_sec, failed|
+    print '.'
+    results[server] ||= []
+    results[server] << req_sec
+  end
+  
+  b.run!
+  puts
+  
+  results.each do |server, concurrencies|
+    g.data(server, concurrencies)    
+  end
+  
+  g.write('bench.png')
+  `open bench.png`
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/bin/thin b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/bin/thin
new file mode 100644 (file)
index 0000000..860107d
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# Thin command line interface script.
+# Run <tt>thin -h</tt> to get more usage.
+
+require 'thin'
+Thin::Runner.new(ARGV).run!
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/adapter.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/adapter.rb
new file mode 100644 (file)
index 0000000..77375fd
--- /dev/null
@@ -0,0 +1,32 @@
+# Run with: ruby adapter.rb
+# Then browse to http://localhost:3000/test
+# and http://localhost:3000/files/adapter.rb
+require 'thin'
+
+class SimpleAdapter
+  def call(env)
+    body = ["hello!"]
+    [
+      200,
+      { 'Content-Type' => 'text/plain' },
+      body
+    ]
+  end
+end
+
+Thin::Server.start('0.0.0.0', 3000) do
+  use Rack::CommonLogger
+  map '/test' do
+    run SimpleAdapter.new
+  end
+  map '/files' do
+    run Rack::File.new('.')
+  end
+end
+
+# You could also start the server like this:
+#
+#   app = Rack::URLMap.new('/test'  => SimpleAdapter.new,
+#                          '/files' => Rack::File.new('.'))
+#   Thin::Server.start('0.0.0.0', 3000, app)
+#
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_app.ru b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_app.ru
new file mode 100644 (file)
index 0000000..4e9be17
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/env rackup -s thin
+# 
+#  async_app.ru
+#  raggi/thin
+#   
+#   A second demo app for async rack + thin app processing!
+#   Now using http status code 100 instead.
+# 
+#  Created by James Tucker on 2008-06-17.
+#  Copyright 2008 James Tucker <raggi@rubyforge.org>.
+#
+#--
+# Benchmark Results:
+#
+# raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/
+# This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
+# Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
+# Copyright 2006 The Apache Software Foundation, http://www.apache.org/
+# 
+# Benchmarking 127.0.0.1 (be patient)
+# Completed 100 requests
+# Completed 200 requests
+# Completed 300 requests
+# Completed 400 requests
+# Finished 500 requests
+# 
+# 
+# Server Software:        thin
+# Server Hostname:        127.0.0.1
+# Server Port:            3000
+# 
+# Document Path:          /
+# Document Length:        12 bytes
+# 
+# Concurrency Level:      100
+# Time taken for tests:   5.263089 seconds
+# Complete requests:      500
+# Failed requests:        0
+# Write errors:           0
+# Total transferred:      47000 bytes
+# HTML transferred:       6000 bytes
+# Requests per second:    95.00 [#/sec] (mean)
+# Time per request:       1052.618 [ms] (mean)
+# Time per request:       10.526 [ms] (mean, across all concurrent requests)
+# Transfer rate:          8.55 [Kbytes/sec] received
+# 
+# Connection Times (ms)
+#               min  mean[+/-sd] median   max
+# Connect:        0    3   2.2      3       8
+# Processing:  1042 1046   3.1   1046    1053
+# Waiting:     1037 1042   3.6   1041    1050
+# Total:       1045 1049   3.1   1049    1057
+# 
+# Percentage of the requests served within a certain time (ms)
+#   50%   1049
+#   66%   1051
+#   75%   1053
+#   80%   1053
+#   90%   1054
+#   95%   1054
+#   98%   1056
+#   99%   1057
+#  100%   1057 (longest request)
+
+class DeferrableBody
+  include EventMachine::Deferrable
+
+  def call(body)
+    body.each do |chunk|
+      @body_callback.call(chunk)
+    end
+  end
+
+  def each &blk
+    @body_callback = blk
+  end
+
+end
+
+class AsyncApp
+  
+  # This is a template async response. N.B. Can't use string for body on 1.9
+  AsyncResponse = [-1, {}, []].freeze
+    
+  def call(env)
+    
+    body = DeferrableBody.new
+    
+    # Get the headers out there asap, let the client know we're alive...
+    EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
+    
+    # Semi-emulate a long db request, instead of a timer, in reality we'd be 
+    # waiting for the response data. Whilst this happens, other connections 
+    # can be serviced.
+    # This could be any callback based thing though, a deferrable waiting on 
+    # IO data, a db request, an http request, an smtp send, whatever.
+    EventMachine::add_timer(1) {
+      body.call ["Woah, async!\n"]
+      
+      EventMachine::next_tick {
+        # This could actually happen any time, you could spawn off to new 
+        # threads, pause as a good looking lady walks by, whatever.
+        # Just shows off how we can defer chunks of data in the body, you can
+        # even call this many times.
+        body.call ["Cheers then!"]
+        body.succeed
+      }
+    }
+    
+    # throw :async # Still works for supporting non-async frameworks...
+    
+    AsyncResponse # May end up in Rack :-)
+  end
+  
+end
+
+# The additions to env for async.connection and async.callback absolutely 
+# destroy the speed of the request if Lint is doing it's checks on env.
+# It is also important to note that an async response will not pass through 
+# any further middleware, as the async response notification has been passed 
+# right up to the webserver, and the callback goes directly there too.
+# Middleware could possibly catch :async, and also provide a different 
+# async.connection and async.callback.
+
+# use Rack::Lint
+run AsyncApp.new
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_chat.ru b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_chat.ru
new file mode 100644 (file)
index 0000000..e9e3ddf
--- /dev/null
@@ -0,0 +1,247 @@
+#!/usr/bin/env rackup -s thin
+# 
+#  async_chat.ru
+#  raggi/thin
+#  
+#  Created by James Tucker on 2008-06-19.
+#  Copyright 2008 James Tucker <raggi@rubyforge.org>.
+
+# Uncomment if appropriate for you..
+EM.epoll
+# EM.kqueue # bug on OS X in 0.12?
+
+class DeferrableBody
+  include EventMachine::Deferrable
+  
+  def initialize
+    @queue = []
+  end
+  
+  def schedule_dequeue
+    return unless @body_callback
+    EventMachine::next_tick do
+      next unless body = @queue.shift
+      body.each do |chunk|
+        @body_callback.call(chunk)
+      end
+      schedule_dequeue unless @queue.empty?
+    end
+  end 
+
+  def call(body)
+    @queue << body
+    schedule_dequeue
+  end
+
+  def each &blk
+    @body_callback = blk
+    schedule_dequeue
+  end
+
+end
+
+class Chat
+  
+  module UserBody
+    attr_accessor :username
+  end
+  
+  def initialize
+    @users = {}
+  end
+  
+  def render_page
+    [] << <<-EOPAGE
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+  <html>
+    <head>
+      <style>
+        body {
+          font-family: sans-serif;
+          margin: 0; 
+          padding: 0;
+          margin-top: 4em;
+          margin-bottom: 1em;
+        }
+        #header {
+          background: silver;
+          height: 4em;
+          width: 100%;
+          position: fixed;
+          top: 0px;
+          border-bottom: 1px solid black;
+          padding-left: 0.5em;
+        }
+        #messages {
+          width: 100%;
+          height: 100%;
+        }
+        .message {
+          margin-left: 1em;
+        }
+        #send_form {
+          position: fixed;
+          bottom: 0px;
+          height: 1em;
+          width: 100%;
+        }
+        #message_box {
+          background: silver;
+          width: 100%;
+          border: 0px;
+          border-top: 1px solid black;
+        }
+        .gray {
+          color: gray;
+        }
+      </style>
+      <script type="text/javascript" src="http://ra66i.org/tmp/jquery-1.2.6.min.js"></script>
+      <script type="text/javascript">
+        XHR = function() {
+          var request = false;
+          try { request = new ActiveXObject('Msxml2.XMLHTTP');    } catch(e) {
+            try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch(e1) {
+                   try {       request = new XMLHttpRequest();                         }       catch(e2) { 
+                     return false; 
+                     }
+                     }
+             }
+          return request;
+        }
+        scroll = function() {
+               window.scrollBy(0,50);
+               setTimeout('scroll()',100);
+        }
+        focus = function() {
+          $('#message_box').focus();
+        }
+        send_message = function(message_box) {
+          xhr = XHR();
+          xhr.open("POST", "/", true); 
+               xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+               xhr.setRequestHeader("X_REQUESTED_WITH", "XMLHttpRequest");
+               xhr.send("message="+escape(message_box.value));
+          scroll();
+          message_box.value = '';
+          focus();
+          return false;
+        }
+        new_message = function(username, message) {
+          // TODO html escape message
+          formatted_message = "<div class='message'>" + username + ": " + message + "</div>";
+          messages_div = $('#messages');
+          $(formatted_message).appendTo(messages_div);
+          scroll();
+          return true;
+        }
+      </script>
+      <title>Async Chat</title>
+    </head>
+    <body>
+      <div id="header">
+        <h1>Async Chat</h1>
+      </div>
+      <div id="messages" onclick="focus();">
+        <span class="gray">Your first message will become your nickname!</span>
+        <span>Users: #{@users.map{|k,u|u.username}.join(', ')}</span>
+      </div>
+      <form id="send_form" onSubmit="return send_message(this.message)">
+        <input type="text" id="message_box" name="message"></input>
+      </form>
+      <script type="text/javascript">focus();</script>
+    </body>
+  </html>
+  EOPAGE
+  end
+  
+  def register_user(user_id, renderer)
+    body = create_user(user_id)
+    body.call render_page
+    body.errback { delete_user user_id }
+    body.callback { delete_user user_id }
+    
+    EventMachine::next_tick do
+      renderer.call [200, {'Content-Type' => 'text/html'}, body]
+    end
+  end
+  
+  def new_message(user_id, message)
+    return unless @users[user_id]
+    if @users[user_id].username == :anonymous
+      username = unique_username(message)
+      log "User: #{user_id} is #{username}"
+      @users[user_id].username = message
+      message = "<span class='gray'>-> #{username} signed on.</span>"
+    end
+    username ||= @users[user_id].username
+    log "User: #{username} sent: #{message}"
+    @users.each do |id, body|
+      EventMachine::next_tick { body.call [js_message(username, message)] }
+    end
+  end
+  
+  private
+  def unique_username(name)
+    name.concat('_') while @users.any? { |id,u| name == u.username }
+    name
+  end
+  
+  def log(str)
+    print str, "\n"
+  end
+  
+  def add_user(id, body)
+    @users[id] = body
+  end
+  
+  def delete_user(id)
+    message = "User: #{id} - #{@users[id].username if @users[id]} disconnected."
+    log message
+    new_message(id, message)
+    @users.delete id
+  end
+  
+  def js_message(username, message)
+    %(<script type="text/javascript">new_message("#{username}","#{message}");</script>)
+  end
+  
+  def create_user(id)
+    message = "User: #{id} connected."
+    log message
+    new_message(id, message)
+    body = DeferrableBody.new
+    body.extend UserBody
+    body.username = :anonymous
+    add_user(id, body)
+    body
+  end
+  
+end
+
+class AsyncChat
+  
+  AsyncResponse = [-1, {}, []].freeze
+  AjaxResponse = [200, {}, []].freeze
+  
+  def initialize
+    @chat = Chat.new
+  end
+  
+  def call(env)  
+    request = Rack::Request.new(env)
+    # TODO - cookie me, baby
+    user_id = request.env['REMOTE_ADDR']
+    if request.xhr?
+      message = request['message']
+      @chat.new_message(user_id, Rack::Utils.escape_html(message))
+      AjaxResponse
+    else
+      renderer = request.env['async.callback']
+      @chat.register_user(user_id, renderer)
+      AsyncResponse
+    end
+  end
+  
+end
+
+run AsyncChat.new
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_tailer.ru b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/async_tailer.ru
new file mode 100644 (file)
index 0000000..467f0df
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/env rackup -s thin
+# 
+#  async_tailer.ru
+#  raggi/thin
+#
+#  Tested with 150 spawned tails on OS X
+#  
+#  Created by James Tucker on 2008-06-18.
+#  Copyright 2008 James Tucker <raggi@rubyforge.org>.
+
+# Uncomment if appropriate for you..
+# EM.epoll
+# EM.kqueue
+
+class DeferrableBody
+  include EventMachine::Deferrable
+  
+  def initialize
+    @queue = []
+    # make sure to flush out the queue before closing the connection
+    callback{
+      until @queue.empty?
+        @queue.shift.each{|chunk| @body_callback.call(chunk) }
+      end
+    }
+  end
+  
+  def schedule_dequeue
+    return unless @body_callback
+    EventMachine::next_tick do
+      next unless body = @queue.shift
+      body.each do |chunk|
+        @body_callback.call(chunk)
+      end
+      schedule_dequeue unless @queue.empty?
+    end
+  end 
+
+  def call(body)
+    @queue << body
+    schedule_dequeue
+  end
+
+  def each &blk
+    @body_callback = blk
+    schedule_dequeue
+  end
+
+end
+
+module TailRenderer
+  attr_accessor :callback
+
+  def receive_data(data)
+    @callback.call([data])
+  end
+
+  def unbind
+    @callback.succeed
+  end
+end
+
+class AsyncTailer
+  
+  AsyncResponse = [-1, {}, []].freeze
+    
+  def call(env)
+    
+    body = DeferrableBody.new
+    
+    EventMachine::next_tick do
+      
+      env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body]
+      
+      body.call ["<h1>Async Tailer</h1><pre>"]
+      
+    end
+    
+    EventMachine::popen('tail -f /var/log/system.log', TailRenderer) do |t|
+      
+      t.callback = body
+      
+      # If for some reason we 'complete' body, close the tail.
+      body.callback do
+        t.close_connection
+      end
+      
+      # If for some reason the client disconnects, close the tail.
+      body.errback do
+        t.close_connection
+      end
+      
+    end
+    
+    AsyncResponse
+  end
+  
+end
+
+run AsyncTailer.new
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/config.ru b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/config.ru
new file mode 100644 (file)
index 0000000..bc655f8
--- /dev/null
@@ -0,0 +1,22 @@
+# Run with: rackup -s thin
+# then browse to http://localhost:9292
+# Or with: thin start -R config.ru
+# then browse to http://localhost:3000
+# 
+# Check Rack::Builder doc for more details on this file format:
+#  http://rack.rubyforge.org/doc/classes/Rack/Builder.html
+require 'thin'
+
+app = proc do |env|
+  # Response body has to respond to each and yield strings
+  # See Rack specs for more info: http://rack.rubyforge.org/doc/files/SPEC.html
+  body = ['hi!']
+  
+  [
+    200,                                        # Status code
+    { 'Content-Type' => 'text/html' },          # Reponse headers
+    body                                        # Body of the response
+  ]
+end
+
+run app
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_sockets b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_sockets
new file mode 100644 (file)
index 0000000..15e9c43
--- /dev/null
@@ -0,0 +1,20 @@
+check process blog1
+    with pidfile /u/apps/blog/shared/pids/thin.14000.pid
+    start program = "ruby thin start -d -e production -u nobody -g nobody -p 14000 -a 127.0.0.1 -P tmp/pids/thin.14000.pid -c /u/apps/blog/current"
+    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14000.pid"
+    if totalmem > 90.0 MB for 5 cycles then restart
+    if failed port 14000 then restart
+    if cpu usage > 95% for 3 cycles then restart
+    if 5 restarts within 5 cycles then timeout
+               group blog
+
+check process blog2
+    with pidfile /u/apps/blog/shared/pids/thin.14001.pid
+    start program = "ruby thin start -d -e production -u nobody -g nobody -p 14001 -a 127.0.0.1 -P tmp/pids/thin.14001.pid -c /u/apps/blog/current"
+    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14001.pid"
+    if totalmem > 90.0 MB for 5 cycles then restart
+    if failed port 14001 then restart
+    if cpu usage > 95% for 3 cycles then restart
+    if 5 restarts within 5 cycles then timeout
+               group blog
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_unixsock b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/monit_unixsock
new file mode 100644 (file)
index 0000000..e5e0e33
--- /dev/null
@@ -0,0 +1,20 @@
+check process blog1
+    with pidfile /u/apps/blog/shared/pids/thin.1.pid
+    start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.1.sock -P tmp/pids/thin.1.pid -c /u/apps/blog/current"
+    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.1.pid"
+    if totalmem > 90.0 MB for 5 cycles then restart
+               if failed unixsocket /u/apps/blog/shared/pids/thin.1.sock then restart
+    if cpu usage > 95% for 3 cycles then restart
+    if 5 restarts within 5 cycles then timeout
+               group blog
+
+check process blog2
+    with pidfile /u/apps/blog/shared/pids/thin.2.pid
+    start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.2.sock -P tmp/pids/thin.2.pid -c /u/apps/blog/current"
+    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.2.pid"
+    if totalmem > 90.0 MB for 5 cycles then restart
+               if failed unixsocket /u/apps/blog/shared/pids/thin.2.sock then restart
+    if cpu usage > 95% for 3 cycles then restart
+    if 5 restarts within 5 cycles then timeout
+               group blog
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/myapp.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/myapp.rb
new file mode 100644 (file)
index 0000000..02e617f
--- /dev/null
@@ -0,0 +1 @@
+Myapp = lambda { |env| [200, {}, 'this is my app!'] }
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/ramaze.ru b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/ramaze.ru
new file mode 100644 (file)
index 0000000..2a19ffb
--- /dev/null
@@ -0,0 +1,12 @@
+# Ramaze Rackup config file.
+# by tmm1
+# Use with --rackup option:
+# 
+#   thin start -r ramaze.ru
+# 
+require 'start'
+
+Ramaze.trait[:essentials].delete Ramaze::Adapter
+Ramaze.start :force => true
+
+run Ramaze::Adapter::Base
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin.god b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin.god
new file mode 100644 (file)
index 0000000..4bbb665
--- /dev/null
@@ -0,0 +1,80 @@
+# == God config file
+# http://god.rubyforge.org/
+# Authors: Gump and michael@glauche.de
+#
+# Config file for god that configures watches for each instance of a thin server for
+# each thin configuration file found in /etc/thin.
+# In order to get it working on Ubuntu, I had to make a change to god as noted at
+# the following blog:
+# http://blog.alexgirard.com/ruby-one-line-to-save-god/
+#
+require 'yaml'
+
+config_path = "/etc/thin"
+
+Dir[config_path + "/*.yml"].each do |file|
+  config = YAML.load_file(file)
+  num_servers = config["servers"] ||= 1
+
+  (0...num_servers).each do |i|
+    # UNIX socket cluster use number 0 to 2 (for 3 servers)
+    # and tcp cluster use port number 3000 to 3002.
+    number = config['socket'] ? i : (config['port'] + i)
+    
+    God.watch do |w|
+      w.group = "thin-" + File.basename(file, ".yml")
+      w.name = w.group + "-#{number}"
+      
+      w.interval = 30.seconds
+      
+      w.uid = config["user"]
+      w.gid = config["group"]
+      
+      w.start = "thin start -C #{file} -o #{number}"
+      w.start_grace = 10.seconds
+      
+      w.stop = "thin stop -C #{file} -o #{number}"
+      w.stop_grace = 10.seconds
+      
+      w.restart = "thin restart -C #{file} -o #{number}"
+
+      pid_path = config["chdir"] + "/" + config["pid"]
+      ext = File.extname(pid_path)
+
+      w.pid_file = pid_path.gsub(/#{ext}$/, ".#{number}#{ext}")
+      
+      w.behavior(:clean_pid_file)
+
+      w.start_if do |start|
+        start.condition(:process_running) do |c|
+          c.interval = 5.seconds
+          c.running = false
+        end
+      end
+
+      w.restart_if do |restart|
+        restart.condition(:memory_usage) do |c|
+          c.above = 150.megabytes
+          c.times = [3,5] # 3 out of 5 intervals
+        end
+
+        restart.condition(:cpu_usage) do |c|
+          c.above = 50.percent
+          c.times = 5
+        end
+      end
+
+      w.lifecycle do |on|
+        on.condition(:flapping) do |c|
+          c.to_state = [:start, :restart]
+          c.times = 5
+          c.within = 5.minutes
+          c.transition = :unmonitored
+          c.retry_in = 10.minutes
+          c.retry_times = 5
+          c.retry_within = 2.hours
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.erb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.erb
new file mode 100644 (file)
index 0000000..c96ac88
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<service_bundle type='manifest' name='thin/<%= service_name %>-production'>
+  <service name='network/thin/<%= service_name %>-production' type='service' version='0'>
+    <!-- Dependancies for all Thin servers. -->
+    <dependency name='fs' grouping='require_all' restart_on='none' type='service'>
+      <service_fmri value='svc:/system/filesystem/local'/>
+    </dependency>
+    <dependency name='net' grouping='require_all' restart_on='none' type='service'>
+      <service_fmri value='svc:/network/loopback'/>
+      <!-- uncomment the following line if you are on an L+ Accelerator since /home is mounted through nfs -->
+      <!--<service_fmri value='svc:/network/nfs/client'/>-->
+    </dependency>
+    <% 0.upto(thin_max_instances - 1) do |instance| %>
+    <!-- instance names can't start with digits. Bummer. -->
+    <instance name='i_<%= instance.to_s %>' enabled='false'>
+      <!-- Cause the multi-user milestone to bring these services up -->
+      <dependent name='<%= service_name %>_<%= instance.to_s %>_multi-user' restart_on='none' grouping='optional_all'>
+        <service_fmri value='svc:/milestone/multi-user'/>
+      </dependent>
+      <exec_method name='start' type='method'
+            exec='/opt/csw/bin/thin -C config/thin.yml --only <%= instance.to_s %> start'
+            timeout_seconds='10'>
+        <method_context working_directory='<%= working_directory %>'>
+          <method_credential user='<%= user %>' group='<%= group %>' />
+          <method_environment>
+            <envvar name='PATH' value='/usr/bin:/bin:/opt/csw/bin' />
+          </method_environment>
+        </method_context>
+      </exec_method>
+      <exec_method name='stop' type='method' exec=':kill' timeout_seconds='10' />
+    </instance>
+    <% end %>
+  </service>
+</service_bundle>
+
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.readme.txt b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/thin_solaris_smf.readme.txt
new file mode 100644 (file)
index 0000000..aea4e93
--- /dev/null
@@ -0,0 +1,150 @@
+Using Thin with Solaris' SMF Monitoring Framework
+- - - - - - - - - - - - - - - - - - - - - - - - -
+
+Solaris uses the Service Management Framework (SMF) at the OS level to manage, monitor, and restart long running processes.  This replaces init scripts, and tools like monit and god.
+
+The sample XML file (thin_solaris_smf.erb) is an example SMF manifest which I use on a Joyent accelerator which runs on OpenSolaris.
+
+This setup will:
+
+- ensure the right dependencies are loaded
+- start n instances of Thin, and monitor each individually.  If any single one dies it will be restarted instantly (test it by killing a single thin instance and it will be back alive before you can type 'ps -ef').
+
+This is better than using clustering since if you start the cluster with SMF it will only notice a problem and restart individual Thin's if ALL of them are dead, at which point it will restart the whole cluster.  This approach makes sure that all of your Thins start together and are monitored and managed independant of each other.  This problem likely exists if you are using god or monit to monitor only the start of the master cluster, and don't then monitor the individual processes started.
+
+This example is in .erb format instead of plain XML since I dynamically generate this file as part of a Capistrano deployment.  In my deploy.rb file I define the variables found in this erb.  Of course you don't need to use this with Capistrano.  Just replace the few ERB variables from the xml file, change its extension, and load that directly in Solaris if you prefer.
+
+Here are some examples for usage to get you started with Capistrano, and Thin:
+
+FILE : config/deploy.rb
+--
+
+  require 'config/accelerator/accelerator_tasks'
+
+  set :application, "yourapp"
+  set :svcadm_bin, "/usr/sbin/svcadm"
+  set :svccfg_bin, "/usr/sbin/svccfg"
+  set :svcs_bin, "/usr/bin/svcs"
+
+  # gets the list of remote service SMF names that we need to start
+  # like (depending on thin_max_instances settings):
+  # svc:/network/thin/yourapp-production:i_0
+  # svc:/network/thin/yourapp-production:i_1
+  # svc:/network/thin/yourapp-production:i_2
+  set :service_list, "`svcs -H -o FMRI svc:network/thin/#{application}-production`"
+
+  # how many Thin instances should be setup to run?
+  # this affects the generated thin smf file, and the nginx vhost conf
+  # need to re-run setup for thin smf and nginx vhost conf when changed
+  set :thin_max_instances, 3
+
+  # OVERRIDE STANDARD TASKS
+  desc "Restart the entire application"
+  deploy.task :restart do
+    accelerator.thin.restart
+    accelerator.nginx.restart
+  end
+
+  desc "Start the entire application"
+  deploy.task :start do
+    accelerator.thin.restart
+    accelerator.nginx.restart
+  end
+
+  desc "Stop the entire application"
+  deploy.task :stop do
+    accelerator.thin.disable
+    accelerator.nginx.disable
+  end
+
+
+FILE : config/accelerator/accelerator_tasks.rb
+--
+
+    desc "Create and deploy Thin SMF config"
+    task :create_thin_smf, :roles => :app do
+      service_name = application
+      working_directory = current_path
+      template = File.read("config/accelerator/thin_solaris_smf.erb")
+      buffer = ERB.new(template).result(binding)
+      put buffer, "#{shared_path}/#{application}-thin-smf.xml"
+      sudo "#{svccfg_bin} import #{shared_path}/#{application}-thin-smf.xml"
+    end
+
+    desc "Delete Thin SMF config"
+    task :delete_thin_smf, :roles => :app do
+      accelerator.thin.disable
+      sudo "#{svccfg_bin} delete /network/thin/#{application}-production"
+    end
+
+    desc "Show all SMF services"
+    task :svcs do
+      run "#{svcs_bin} -a" do |ch, st, data|
+        puts data
+      end
+    end
+
+    desc "Shows all non-functional SMF services"
+    task :svcs_broken do
+      run "#{svcs_bin} -vx" do |ch, st, data|
+        puts data
+      end
+    end
+
+
+    namespace :thin do
+
+      desc "Disable all Thin servers"
+      task :disable, :roles => :app do
+        # temporarily disable, until next reboot (-t)
+        sudo "#{svcadm_bin} disable -t #{service_list}"
+      end
+
+      desc "Enable all Thin servers"
+      task :enable, :roles => :app do
+        # start the app with all recursive dependencies
+        sudo "#{svcadm_bin} enable -r #{service_list}"
+      end
+
+      desc "Restart all Thin servers"
+      task :restart, :roles => :app do
+        # svcadm restart doesn't seem to work right, so we'll brute force it
+        disable
+        enable
+      end
+
+    end # namespace thin
+
+
+FILE : config/thin.yml
+--
+
+---
+pid: tmp/pids/thin.pid
+socket: /tmp/thin.sock
+log: log/thin.log
+max_conns: 1024
+timeout: 30
+chdir: /your/app/dir/rails/root
+environment: production
+max_persistent_conns: 512
+daemonize: true
+servers: 3
+
+
+FILE : config/accelerator/thin_solaris_smf.erb
+--
+This is of course an example.  It works for me, but YMMV
+
+You may need to change this line to match your environment and config:
+  exec='/opt/csw/bin/thin -C config/thin.yml --only <%= instance.to_s %> start'
+
+
+CONTRIBUTE:
+
+If you see problems or enhancements for this approach please send me an email at glenn [at] rempe dot us.  Sadly, I won't be able to provide support for this example as time and my limited Solaris admin skills won't allow.
+
+Cheers,
+
+Glenn Rempe
+2008/03/20
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/vlad.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/example/vlad.rake
new file mode 100644 (file)
index 0000000..51b4ae6
--- /dev/null
@@ -0,0 +1,64 @@
+# $GEM_HOME/gems/vlad-1.2.0/lib/vlad/thin.rb
+# Thin tasks for Vlad the Deployer
+# By cnantais
+require 'vlad'
+
+namespace :vlad do
+  ##
+  # Thin app server
+
+  set :thin_address,       "127.0.0.1"
+  set :thin_command,       'thin'
+  set(:thin_conf)          { "#{shared_path}/thin_cluster.conf" }
+  set :thin_environment,   "production"
+  set :thin_group,         nil
+  set :thin_log_file,      nil
+  set :thin_pid_file,      nil
+  set :thin_port,          nil
+  set :thin_socket,        nil
+  set :thin_prefix,        nil
+  set :thin_servers,       2
+  set :thin_user,          nil
+
+  desc "Prepares application servers for deployment. thin
+configuration is set via the thin_* variables.".cleanup
+
+  remote_task :setup_app, :roles => :app do
+  
+    raise(ArgumentError, "Please provide either thin_socket or thin_port") if thin_port.nil? && thin_socket.nil?
+  
+    cmd = [
+           "#{thin_command} config",
+           "-s #{thin_servers}",
+           ("-S #{thin_socket}" if thin_socket),
+           "-e #{thin_environment}",
+           "-a #{thin_address}",
+           "-c #{current_path}",
+           "-C #{thin_conf}",
+           ("-P #{thin_pid_file}" if thin_pid_file),
+           ("-l #{thin_log_file}" if thin_log_file),
+           ("--user #{thin_user}" if thin_user),
+           ("--group #{thin_group}" if thin_group),
+           ("--prefix #{thin_prefix}" if thin_prefix),
+           ("-p #{thin_port}" if thin_port),
+          ].compact.join ' '
+
+    run cmd
+  end
+
+  def thin(cmd) # :nodoc:
+    "#{thin_command} #{cmd} -C #{thin_conf}"
+  end
+
+  desc "Restart the app servers"
+
+  remote_task :start_app, :roles => :app do
+    run thin("restart -s #{thin_servers}")
+  end
+
+  desc "Stop the app servers"
+
+  remote_task :stop_app, :roles => :app do
+    run thin("stop -s #{thin_servers}")
+  end
+end
   safe = ("$" | "-" | "_" | ".");
   extra = ("!" | "*" | "'" | "(" | ")" | ",");
   reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
-  unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
+  sorta_safe = ("\"" | "<" | ">");
+  unsafe = (CTL | " " | "#" | "%" | sorta_safe);
   national = any -- (alpha | digit | reserved | extra | safe | unsafe);
   unreserved = (alpha | digit | safe | extra | national);
-  escape = ("%" xdigit xdigit);
-  uchar = (unreserved | escape);
+  escape = ("%" "u"? xdigit xdigit);
+  uchar = (unreserved | escape | sorta_safe);
   pchar = (uchar | ":" | "@" | "&" | "=" | "+");
   tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
 
@@ -30,7 +31,7 @@
   query = ( uchar | reserved )* %query_string ;
   param = ( pchar | "/" )* ;
   params = ( param ( ";" param )* ) ;
-  rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
+  rel_path = ( path? (";" params)? %request_path) ("?" %start_query query)?;
   absolute_path = ( "/"+ rel_path );
 
   Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/extconf.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/ext/thin_parser/extconf.rb
new file mode 100644 (file)
index 0000000..f83a75a
--- /dev/null
@@ -0,0 +1,6 @@
+require 'mkmf'
+
+dir_config("thin_parser")
+have_library("c", "main")
+
+create_makefile("thin_parser")
@@ -1,9 +1,10 @@
-#line 1 "http11_parser.rl"
+
+#line 1 "parser.rl"
 /**
  * Copyright (c) 2005 Zed A. Shaw
  * You can redistribute it and/or modify it under the same terms as Ruby.
  */
-#include "http11_parser.h"
+#include "parser.h"
 #include <stdio.h>
 #include <assert.h>
 #include <stdlib.h>
 
 /** Machine **/
 
-#line 74 "http11_parser.rl"
+
+#line 81 "parser.rl"
 
 
 /** Data **/
 
-#line 25 "http11_parser.c"
+#line 27 "parser.c"
 static const int http_parser_start = 1;
-static const int http_parser_first_final = 57;
+static const int http_parser_first_final = 58;
 static const int http_parser_error = 0;
 
 static const int http_parser_en_main = 1;
 
-#line 78 "http11_parser.rl"
 
-int http_parser_init(http_parser *parser)  {
+#line 85 "parser.rl"
+
+int thin_http_parser_init(http_parser *parser)  {
   int cs = 0;
   
-#line 37 "http11_parser.c"
+#line 40 "parser.c"
        {
        cs = http_parser_start;
        }
-#line 82 "http11_parser.rl"
+
+#line 89 "parser.rl"
   parser->cs = cs;
   parser->body_start = 0;
   parser->content_len = 0;
@@ -51,7 +55,7 @@ int http_parser_init(http_parser *parser)  {
 
 
 /** exec **/
-size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
+size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
   const char *p, *pe;
   int cs = parser->cs;
 
@@ -65,10 +69,10 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
 
 
   
-#line 69 "http11_parser.c"
+#line 73 "parser.c"
        {
        if ( p == pe )
-               goto _out;
+               goto _test_eof;
        switch ( cs )
        {
 case 1:
@@ -86,42 +90,44 @@ case 1:
                goto tr0;
        goto st0;
 st0:
-       goto _out0;
+cs = 0;
+       goto _out;
 tr0:
-#line 22 "http11_parser.rl"
+#line 22 "parser.rl"
        {MARK(mark, p); }
        goto st2;
 st2:
        if ( ++p == pe )
-               goto _out2;
+               goto _test_eof2;
 case 2:
-#line 99 "http11_parser.c"
+#line 104 "parser.c"
        switch( (*p) ) {
                case 32: goto tr2;
-               case 36: goto st38;
-               case 95: goto st38;
+               case 36: goto st39;
+               case 95: goto st39;
        }
        if ( (*p) < 48 ) {
                if ( 45 <= (*p) && (*p) <= 46 )
-                       goto st38;
+                       goto st39;
        } else if ( (*p) > 57 ) {
                if ( 65 <= (*p) && (*p) <= 90 )
-                       goto st38;
+                       goto st39;
        } else
-               goto st38;
+               goto st39;
        goto st0;
 tr2:
-#line 36 "http11_parser.rl"
+#line 36 "parser.rl"
        { 
-    if(parser->request_method != NULL) 
+    if (parser->request_method != NULL) {
       parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st3;
 st3:
        if ( ++p == pe )
-               goto _out3;
+               goto _test_eof3;
 case 3:
-#line 125 "http11_parser.c"
+#line 131 "parser.c"
        switch( (*p) ) {
                case 42: goto tr4;
                case 43: goto tr5;
@@ -138,122 +144,140 @@ case 3:
                goto tr5;
        goto st0;
 tr4:
-#line 22 "http11_parser.rl"
+#line 22 "parser.rl"
        {MARK(mark, p); }
        goto st4;
 st4:
        if ( ++p == pe )
-               goto _out4;
+               goto _test_eof4;
 case 4:
-#line 149 "http11_parser.c"
+#line 155 "parser.c"
        switch( (*p) ) {
                case 32: goto tr8;
                case 35: goto tr9;
        }
        goto st0;
 tr8:
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st5;
-tr30:
-#line 44 "http11_parser.rl"
+tr31:
+#line 22 "parser.rl"
+       {MARK(mark, p); }
+#line 46 "parser.rl"
+       { 
+    if (parser->fragment != NULL) {
+      parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
+  }
+       goto st5;
+tr34:
+#line 46 "parser.rl"
        { 
-    if(parser->fragment != NULL)
+    if (parser->fragment != NULL) {
       parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st5;
-tr40:
-#line 60 "http11_parser.rl"
+tr44:
+#line 65 "parser.rl"
        {
-    if(parser->request_path != NULL)
+    if (parser->request_path != NULL) {
       parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st5;
 tr51:
-#line 49 "http11_parser.rl"
+#line 52 "parser.rl"
        {MARK(query_start, p); }
-#line 50 "http11_parser.rl"
+#line 53 "parser.rl"
        { 
-    if(parser->query_string != NULL)
+    if (parser->query_string != NULL) {
       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st5;
 tr55:
-#line 50 "http11_parser.rl"
+#line 53 "parser.rl"
        { 
-    if(parser->query_string != NULL)
+    if (parser->query_string != NULL) {
       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st5;
 st5:
        if ( ++p == pe )
-               goto _out5;
+               goto _test_eof5;
 case 5:
-#line 211 "http11_parser.c"
+#line 235 "parser.c"
        if ( (*p) == 72 )
                goto tr10;
        goto st0;
 tr10:
-#line 22 "http11_parser.rl"
+#line 22 "parser.rl"
        {MARK(mark, p); }
        goto st6;
 st6:
        if ( ++p == pe )
-               goto _out6;
+               goto _test_eof6;
 case 6:
-#line 223 "http11_parser.c"
+#line 247 "parser.c"
        if ( (*p) == 84 )
                goto st7;
        goto st0;
 st7:
        if ( ++p == pe )
-               goto _out7;
+               goto _test_eof7;
 case 7:
        if ( (*p) == 84 )
                goto st8;
        goto st0;
 st8:
        if ( ++p == pe )
-               goto _out8;
+               goto _test_eof8;
 case 8:
        if ( (*p) == 80 )
                goto st9;
        goto st0;
 st9:
        if ( ++p == pe )
-               goto _out9;
+               goto _test_eof9;
 case 9:
        if ( (*p) == 47 )
                goto st10;
        goto st0;
 st10:
        if ( ++p == pe )
-               goto _out10;
+               goto _test_eof10;
 case 10:
        if ( 48 <= (*p) && (*p) <= 57 )
                goto st11;
        goto st0;
 st11:
        if ( ++p == pe )
-               goto _out11;
+               goto _test_eof11;
 case 11:
        if ( (*p) == 46 )
                goto st12;
@@ -262,14 +286,14 @@ case 11:
        goto st0;
 st12:
        if ( ++p == pe )
-               goto _out12;
+               goto _test_eof12;
 case 12:
        if ( 48 <= (*p) && (*p) <= 57 )
                goto st13;
        goto st0;
 st13:
        if ( ++p == pe )
-               goto _out13;
+               goto _test_eof13;
 case 13:
        if ( (*p) == 13 )
                goto tr18;
@@ -277,31 +301,42 @@ case 13:
                goto st13;
        goto st0;
 tr18:
-#line 55 "http11_parser.rl"
+#line 59 "parser.rl"
        {       
-    if(parser->http_version != NULL)
+    if (parser->http_version != NULL) {
       parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st14;
 tr26:
-#line 31 "http11_parser.rl"
+#line 30 "parser.rl"
+       { MARK(mark, p); }
+#line 31 "parser.rl"
        { 
-    if(parser->http_field != NULL) {
+    if (parser->http_field != NULL) {
+      parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+    }
+  }
+       goto st14;
+tr29:
+#line 31 "parser.rl"
+       { 
+    if (parser->http_field != NULL) {
       parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
     }
   }
        goto st14;
 st14:
        if ( ++p == pe )
-               goto _out14;
+               goto _test_eof14;
 case 14:
-#line 299 "http11_parser.c"
+#line 334 "parser.c"
        if ( (*p) == 10 )
                goto st15;
        goto st0;
 st15:
        if ( ++p == pe )
-               goto _out15;
+               goto _test_eof15;
 case 15:
        switch( (*p) ) {
                case 13: goto st16;
@@ -329,35 +364,36 @@ case 15:
        goto st0;
 st16:
        if ( ++p == pe )
-               goto _out16;
+               goto _test_eof16;
 case 16:
        if ( (*p) == 10 )
                goto tr22;
        goto st0;
 tr22:
-#line 65 "http11_parser.rl"
+#line 71 "parser.rl"
        { 
     parser->body_start = p - buffer + 1; 
-    if(parser->header_done != NULL)
+    if (parser->header_done != NULL) {
       parser->header_done(parser->data, p + 1, pe - p - 1);
-    goto _out57;
+    }
+    {p++; cs = 58; goto _out;}
   }
-       goto st57;
-st57:
+       goto st58;
+st58:
        if ( ++p == pe )
-               goto _out57;
-case 57:
-#line 351 "http11_parser.c"
+               goto _test_eof58;
+case 58:
+#line 387 "parser.c"
        goto st0;
 tr21:
-#line 25 "http11_parser.rl"
+#line 25 "parser.rl"
        { MARK(field_start, p); }
        goto st17;
 st17:
        if ( ++p == pe )
-               goto _out17;
+               goto _test_eof17;
 case 17:
-#line 361 "http11_parser.c"
+#line 397 "parser.c"
        switch( (*p) ) {
                case 33: goto st17;
                case 58: goto tr24;
@@ -383,131 +419,132 @@ case 17:
                goto st17;
        goto st0;
 tr24:
-#line 26 "http11_parser.rl"
+#line 26 "parser.rl"
        { 
     parser->field_len = LEN(field_start, p);
   }
        goto st18;
 tr27:
-#line 30 "http11_parser.rl"
+#line 30 "parser.rl"
        { MARK(mark, p); }
        goto st18;
 st18:
        if ( ++p == pe )
-               goto _out18;
+               goto _test_eof18;
 case 18:
-#line 400 "http11_parser.c"
+#line 436 "parser.c"
        switch( (*p) ) {
                case 13: goto tr26;
                case 32: goto tr27;
        }
        goto tr25;
 tr25:
-#line 30 "http11_parser.rl"
+#line 30 "parser.rl"
        { MARK(mark, p); }
        goto st19;
 st19:
        if ( ++p == pe )
-               goto _out19;
+               goto _test_eof19;
 case 19:
-#line 414 "http11_parser.c"
+#line 450 "parser.c"
        if ( (*p) == 13 )
-               goto tr26;
+               goto tr29;
        goto st19;
 tr9:
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st20;
-tr41:
-#line 60 "http11_parser.rl"
+tr45:
+#line 65 "parser.rl"
        {
-    if(parser->request_path != NULL)
+    if (parser->request_path != NULL) {
       parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st20;
 tr52:
-#line 49 "http11_parser.rl"
+#line 52 "parser.rl"
        {MARK(query_start, p); }
-#line 50 "http11_parser.rl"
+#line 53 "parser.rl"
        { 
-    if(parser->query_string != NULL)
+    if (parser->query_string != NULL) {
       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st20;
 tr56:
-#line 50 "http11_parser.rl"
+#line 53 "parser.rl"
        { 
-    if(parser->query_string != NULL)
+    if (parser->query_string != NULL) {
       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+    }
   }
-#line 40 "http11_parser.rl"
-       { 
-    if(parser->request_uri != NULL)
+#line 41 "parser.rl"
+       {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    }
   }
        goto st20;
 st20:
        if ( ++p == pe )
-               goto _out20;
+               goto _test_eof20;
 case 20:
-#line 467 "http11_parser.c"
+#line 510 "parser.c"
        switch( (*p) ) {
-               case 32: goto tr30;
-               case 37: goto tr31;
-               case 60: goto st0;
-               case 62: goto st0;
+               case 32: goto tr31;
+               case 35: goto st0;
+               case 37: goto tr32;
                case 127: goto st0;
        }
-       if ( (*p) > 31 ) {
-               if ( 34 <= (*p) && (*p) <= 35 )
-                       goto st0;
-       } else if ( (*p) >= 0 )
+       if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
-       goto tr29;
-tr29:
-#line 22 "http11_parser.rl"
+       goto tr30;
+tr30:
+#line 22 "parser.rl"
        {MARK(mark, p); }
        goto st21;
 st21:
        if ( ++p == pe )
-               goto _out21;
+               goto _test_eof21;
 case 21:
-#line 489 "http11_parser.c"
+#line 528 "parser.c"
        switch( (*p) ) {
-               case 32: goto tr30;
+               case 32: goto tr34;
+               case 35: goto st0;
                case 37: goto st22;
-               case 60: goto st0;
-               case 62: goto st0;
                case 127: goto st0;
        }
-       if ( (*p) > 31 ) {
-               if ( 34 <= (*p) && (*p) <= 35 )
-                       goto st0;
-       } else if ( (*p) >= 0 )
+       if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
        goto st21;
-tr31:
-#line 22 "http11_parser.rl"
+tr32:
+#line 22 "parser.rl"
        {MARK(mark, p); }
        goto st22;
 st22:
        if ( ++p == pe )
-               goto _out22;
+               goto _test_eof22;
 case 22:
-#line 511 "http11_parser.c"
+#line 546 "parser.c"
+       if ( (*p) == 117 )
+               goto st24;
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
                        goto st23;
@@ -519,7 +556,7 @@ case 22:
        goto st0;
 st23:
        if ( ++p == pe )
-               goto _out23;
+               goto _test_eof23;
 case 23:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
@@ -530,118 +567,94 @@ case 23:
        } else
                goto st21;
        goto st0;
-tr5:
-#line 22 "http11_parser.rl"
-       {MARK(mark, p); }
-       goto st24;
 st24:
        if ( ++p == pe )
-               goto _out24;
+               goto _test_eof24;
 case 24:
-#line 542 "http11_parser.c"
+       if ( (*p) < 65 ) {
+               if ( 48 <= (*p) && (*p) <= 57 )
+                       goto st23;
+       } else if ( (*p) > 70 ) {
+               if ( 97 <= (*p) && (*p) <= 102 )
+                       goto st23;
+       } else
+               goto st23;
+       goto st0;
+tr5:
+#line 22 "parser.rl"
+       {MARK(mark, p); }
+       goto st25;
+st25:
+       if ( ++p == pe )
+               goto _test_eof25;
+case 25:
+#line 592 "parser.c"
        switch( (*p) ) {
-               case 43: goto st24;
-               case 58: goto st25;
+               case 43: goto st25;
+               case 58: goto st26;
        }
        if ( (*p) < 48 ) {
                if ( 45 <= (*p) && (*p) <= 46 )
-                       goto st24;
+                       goto st25;
        } else if ( (*p) > 57 ) {
                if ( (*p) > 90 ) {
                        if ( 97 <= (*p) && (*p) <= 122 )
-                               goto st24;
+                               goto st25;
                } else if ( (*p) >= 65 )
-                       goto st24;
+                       goto st25;
        } else
-               goto st24;
+               goto st25;
        goto st0;
 tr7:
-#line 22 "http11_parser.rl"
+#line 22 "parser.rl"
        {MARK(mark, p); }
-       goto st25;
-st25:
+       goto st26;
+st26:
        if ( ++p == pe )
-               goto _out25;
-case 25:
-#line 567 "http11_parser.c"
+               goto _test_eof26;
+case 26:
+#line 617 "parser.c"
        switch( (*p) ) {
                case 32: goto tr8;
-               case 34: goto st0;
                case 35: goto tr9;
-               case 37: goto st26;
-               case 60: goto st0;
-               case 62: goto st0;
+               case 37: goto st27;
                case 127: goto st0;
        }
        if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
-       goto st25;
-st26:
-       if ( ++p == pe )
-               goto _out26;
-case 26:
-       if ( (*p) < 65 ) {
-               if ( 48 <= (*p) && (*p) <= 57 )
-                       goto st27;
-       } else if ( (*p) > 70 ) {
-               if ( 97 <= (*p) && (*p) <= 102 )
-                       goto st27;
-       } else
-               goto st27;
-       goto st0;
+       goto st26;
 st27:
        if ( ++p == pe )
-               goto _out27;
+               goto _test_eof27;
 case 27:
+       if ( (*p) == 117 )
+               goto st29;
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
-                       goto st25;
+                       goto st28;
        } else if ( (*p) > 70 ) {
                if ( 97 <= (*p) && (*p) <= 102 )
-                       goto st25;
+                       goto st28;
        } else
-               goto st25;
+               goto st28;
        goto st0;
-tr6:
-#line 22 "http11_parser.rl"
-       {MARK(mark, p); }
-       goto st28;
 st28:
        if ( ++p == pe )
-               goto _out28;
+               goto _test_eof28;
 case 28:
-#line 614 "http11_parser.c"
-       switch( (*p) ) {
-               case 32: goto tr40;
-               case 34: goto st0;
-               case 35: goto tr41;
-               case 37: goto st29;
-               case 59: goto tr43;
-               case 60: goto st0;
-               case 62: goto st0;
-               case 63: goto tr44;
-               case 127: goto st0;
-       }
-       if ( 0 <= (*p) && (*p) <= 31 )
-               goto st0;
-       goto st28;
-st29:
-       if ( ++p == pe )
-               goto _out29;
-case 29:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
-                       goto st30;
+                       goto st26;
        } else if ( (*p) > 70 ) {
                if ( 97 <= (*p) && (*p) <= 102 )
-                       goto st30;
+                       goto st26;
        } else
-               goto st30;
+               goto st26;
        goto st0;
-st30:
+st29:
        if ( ++p == pe )
-               goto _out30;
-case 30:
+               goto _test_eof29;
+case 29:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
                        goto st28;
@@ -651,111 +664,117 @@ case 30:
        } else
                goto st28;
        goto st0;
-tr43:
-#line 60 "http11_parser.rl"
-       {
-    if(parser->request_path != NULL)
-      parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
-  }
-       goto st31;
-st31:
+tr6:
+#line 22 "parser.rl"
+       {MARK(mark, p); }
+       goto st30;
+st30:
        if ( ++p == pe )
-               goto _out31;
-case 31:
-#line 666 "http11_parser.c"
+               goto _test_eof30;
+case 30:
+#line 676 "parser.c"
        switch( (*p) ) {
-               case 32: goto tr8;
-               case 34: goto st0;
-               case 35: goto tr9;
-               case 37: goto st32;
-               case 60: goto st0;
-               case 62: goto st0;
-               case 63: goto st34;
+               case 32: goto tr44;
+               case 35: goto tr45;
+               case 37: goto st31;
+               case 63: goto tr47;
                case 127: goto st0;
        }
        if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
-       goto st31;
+       goto st30;
+st31:
+       if ( ++p == pe )
+               goto _test_eof31;
+case 31:
+       if ( (*p) == 117 )
+               goto st33;
+       if ( (*p) < 65 ) {
+               if ( 48 <= (*p) && (*p) <= 57 )
+                       goto st32;
+       } else if ( (*p) > 70 ) {
+               if ( 97 <= (*p) && (*p) <= 102 )
+                       goto st32;
+       } else
+               goto st32;
+       goto st0;
 st32:
        if ( ++p == pe )
-               goto _out32;
+               goto _test_eof32;
 case 32:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
-                       goto st33;
+                       goto st30;
        } else if ( (*p) > 70 ) {
                if ( 97 <= (*p) && (*p) <= 102 )
-                       goto st33;
+                       goto st30;
        } else
-               goto st33;
+               goto st30;
        goto st0;
 st33:
        if ( ++p == pe )
-               goto _out33;
+               goto _test_eof33;
 case 33:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
-                       goto st31;
+                       goto st32;
        } else if ( (*p) > 70 ) {
                if ( 97 <= (*p) && (*p) <= 102 )
-                       goto st31;
+                       goto st32;
        } else
-               goto st31;
+               goto st32;
        goto st0;
-tr44:
-#line 60 "http11_parser.rl"
+tr47:
+#line 65 "parser.rl"
        {
-    if(parser->request_path != NULL)
+    if (parser->request_path != NULL) {
       parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+    }
   }
        goto st34;
 st34:
        if ( ++p == pe )
-               goto _out34;
+               goto _test_eof34;
 case 34:
-#line 717 "http11_parser.c"
+#line 740 "parser.c"
        switch( (*p) ) {
                case 32: goto tr51;
-               case 34: goto st0;
                case 35: goto tr52;
                case 37: goto tr53;
-               case 60: goto st0;
-               case 62: goto st0;
                case 127: goto st0;
        }
        if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
        goto tr50;
 tr50:
-#line 49 "http11_parser.rl"
+#line 52 "parser.rl"
        {MARK(query_start, p); }
        goto st35;
 st35:
        if ( ++p == pe )
-               goto _out35;
+               goto _test_eof35;
 case 35:
-#line 738 "http11_parser.c"
+#line 758 "parser.c"
        switch( (*p) ) {
                case 32: goto tr55;
-               case 34: goto st0;
                case 35: goto tr56;
                case 37: goto st36;
-               case 60: goto st0;
-               case 62: goto st0;
                case 127: goto st0;
        }
        if ( 0 <= (*p) && (*p) <= 31 )
                goto st0;
        goto st35;
 tr53:
-#line 49 "http11_parser.rl"
+#line 52 "parser.rl"
        {MARK(query_start, p); }
        goto st36;
 st36:
        if ( ++p == pe )
-               goto _out36;
+               goto _test_eof36;
 case 36:
-#line 759 "http11_parser.c"
+#line 776 "parser.c"
+       if ( (*p) == 117 )
+               goto st38;
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
                        goto st37;
@@ -767,7 +786,7 @@ case 36:
        goto st0;
 st37:
        if ( ++p == pe )
-               goto _out37;
+               goto _test_eof37;
 case 37:
        if ( (*p) < 65 ) {
                if ( 48 <= (*p) && (*p) <= 57 )
@@ -780,25 +799,20 @@ case 37:
        goto st0;
 st38:
        if ( ++p == pe )
-               goto _out38;
+               goto _test_eof38;
 case 38:
-       switch( (*p) ) {
-               case 32: goto tr2;
-               case 36: goto st39;
-               case 95: goto st39;
-       }
-       if ( (*p) < 48 ) {
-               if ( 45 <= (*p) && (*p) <= 46 )
-                       goto st39;
-       } else if ( (*p) > 57 ) {
-               if ( 65 <= (*p) && (*p) <= 90 )
-                       goto st39;
+       if ( (*p) < 65 ) {
+               if ( 48 <= (*p) && (*p) <= 57 )
+                       goto st37;
+       } else if ( (*p) > 70 ) {
+               if ( 97 <= (*p) && (*p) <= 102 )
+                       goto st37;
        } else
-               goto st39;
+               goto st37;
        goto st0;
 st39:
        if ( ++p == pe )
-               goto _out39;
+               goto _test_eof39;
 case 39:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -816,7 +830,7 @@ case 39:
        goto st0;
 st40:
        if ( ++p == pe )
-               goto _out40;
+               goto _test_eof40;
 case 40:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -834,7 +848,7 @@ case 40:
        goto st0;
 st41:
        if ( ++p == pe )
-               goto _out41;
+               goto _test_eof41;
 case 41:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -852,7 +866,7 @@ case 41:
        goto st0;
 st42:
        if ( ++p == pe )
-               goto _out42;
+               goto _test_eof42;
 case 42:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -870,7 +884,7 @@ case 42:
        goto st0;
 st43:
        if ( ++p == pe )
-               goto _out43;
+               goto _test_eof43;
 case 43:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -888,7 +902,7 @@ case 43:
        goto st0;
 st44:
        if ( ++p == pe )
-               goto _out44;
+               goto _test_eof44;
 case 44:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -906,7 +920,7 @@ case 44:
        goto st0;
 st45:
        if ( ++p == pe )
-               goto _out45;
+               goto _test_eof45;
 case 45:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -924,7 +938,7 @@ case 45:
        goto st0;
 st46:
        if ( ++p == pe )
-               goto _out46;
+               goto _test_eof46;
 case 46:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -942,7 +956,7 @@ case 46:
        goto st0;
 st47:
        if ( ++p == pe )
-               goto _out47;
+               goto _test_eof47;
 case 47:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -960,7 +974,7 @@ case 47:
        goto st0;
 st48:
        if ( ++p == pe )
-               goto _out48;
+               goto _test_eof48;
 case 48:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -978,7 +992,7 @@ case 48:
        goto st0;
 st49:
        if ( ++p == pe )
-               goto _out49;
+               goto _test_eof49;
 case 49:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -996,7 +1010,7 @@ case 49:
        goto st0;
 st50:
        if ( ++p == pe )
-               goto _out50;
+               goto _test_eof50;
 case 50:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1014,7 +1028,7 @@ case 50:
        goto st0;
 st51:
        if ( ++p == pe )
-               goto _out51;
+               goto _test_eof51;
 case 51:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1032,7 +1046,7 @@ case 51:
        goto st0;
 st52:
        if ( ++p == pe )
-               goto _out52;
+               goto _test_eof52;
 case 52:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1050,7 +1064,7 @@ case 52:
        goto st0;
 st53:
        if ( ++p == pe )
-               goto _out53;
+               goto _test_eof53;
 case 53:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1068,7 +1082,7 @@ case 53:
        goto st0;
 st54:
        if ( ++p == pe )
-               goto _out54;
+               goto _test_eof54;
 case 54:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1086,7 +1100,7 @@ case 54:
        goto st0;
 st55:
        if ( ++p == pe )
-               goto _out55;
+               goto _test_eof55;
 case 55:
        switch( (*p) ) {
                case 32: goto tr2;
@@ -1104,73 +1118,93 @@ case 55:
        goto st0;
 st56:
        if ( ++p == pe )
-               goto _out56;
+               goto _test_eof56;
 case 56:
+       switch( (*p) ) {
+               case 32: goto tr2;
+               case 36: goto st57;
+               case 95: goto st57;
+       }
+       if ( (*p) < 48 ) {
+               if ( 45 <= (*p) && (*p) <= 46 )
+                       goto st57;
+       } else if ( (*p) > 57 ) {
+               if ( 65 <= (*p) && (*p) <= 90 )
+                       goto st57;
+       } else
+               goto st57;
+       goto st0;
+st57:
+       if ( ++p == pe )
+               goto _test_eof57;
+case 57:
        if ( (*p) == 32 )
                goto tr2;
        goto st0;
        }
-       _out0: cs = 0; goto _out
-       _out2: cs = 2; goto _out
-       _out3: cs = 3; goto _out
-       _out4: cs = 4; goto _out
-       _out5: cs = 5; goto _out
-       _out6: cs = 6; goto _out
-       _out7: cs = 7; goto _out
-       _out8: cs = 8; goto _out
-       _out9: cs = 9; goto _out
-       _out10: cs = 10; goto _out
-       _out11: cs = 11; goto _out
-       _out12: cs = 12; goto _out
-       _out13: cs = 13; goto _out
-       _out14: cs = 14; goto _out
-       _out15: cs = 15; goto _out
-       _out16: cs = 16; goto _out
-       _out57: cs = 57; goto _out
-       _out17: cs = 17; goto _out
-       _out18: cs = 18; goto _out
-       _out19: cs = 19; goto _out
-       _out20: cs = 20; goto _out
-       _out21: cs = 21; goto _out
-       _out22: cs = 22; goto _out
-       _out23: cs = 23; goto _out
-       _out24: cs = 24; goto _out
-       _out25: cs = 25; goto _out
-       _out26: cs = 26; goto _out
-       _out27: cs = 27; goto _out
-       _out28: cs = 28; goto _out
-       _out29: cs = 29; goto _out
-       _out30: cs = 30; goto _out
-       _out31: cs = 31; goto _out
-       _out32: cs = 32; goto _out
-       _out33: cs = 33; goto _out
-       _out34: cs = 34; goto _out
-       _out35: cs = 35; goto _out
-       _out36: cs = 36; goto _out
-       _out37: cs = 37; goto _out
-       _out38: cs = 38; goto _out
-       _out39: cs = 39; goto _out
-       _out40: cs = 40; goto _out
-       _out41: cs = 41; goto _out
-       _out42: cs = 42; goto _out
-       _out43: cs = 43; goto _out
-       _out44: cs = 44; goto _out
-       _out45: cs = 45; goto _out
-       _out46: cs = 46; goto _out
-       _out47: cs = 47; goto _out
-       _out48: cs = 48; goto _out
-       _out49: cs = 49; goto _out
-       _out50: cs = 50; goto _out
-       _out51: cs = 51; goto _out
-       _out52: cs = 52; goto _out
-       _out53: cs = 53; goto _out
-       _out54: cs = 54; goto _out
-       _out55: cs = 55; goto _out
-       _out56: cs = 56; goto _out
+       _test_eof2: cs = 2; goto _test_eof
+       _test_eof3: cs = 3; goto _test_eof
+       _test_eof4: cs = 4; goto _test_eof
+       _test_eof5: cs = 5; goto _test_eof
+       _test_eof6: cs = 6; goto _test_eof
+       _test_eof7: cs = 7; goto _test_eof
+       _test_eof8: cs = 8; goto _test_eof
+       _test_eof9: cs = 9; goto _test_eof
+       _test_eof10: cs = 10; goto _test_eof
+       _test_eof11: cs = 11; goto _test_eof
+       _test_eof12: cs = 12; goto _test_eof
+       _test_eof13: cs = 13; goto _test_eof
+       _test_eof14: cs = 14; goto _test_eof
+       _test_eof15: cs = 15; goto _test_eof
+       _test_eof16: cs = 16; goto _test_eof
+       _test_eof58: cs = 58; goto _test_eof
+       _test_eof17: cs = 17; goto _test_eof
+       _test_eof18: cs = 18; goto _test_eof
+       _test_eof19: cs = 19; goto _test_eof
+       _test_eof20: cs = 20; goto _test_eof
+       _test_eof21: cs = 21; goto _test_eof
+       _test_eof22: cs = 22; goto _test_eof
+       _test_eof23: cs = 23; goto _test_eof
+       _test_eof24: cs = 24; goto _test_eof
+       _test_eof25: cs = 25; goto _test_eof
+       _test_eof26: cs = 26; goto _test_eof
+       _test_eof27: cs = 27; goto _test_eof
+       _test_eof28: cs = 28; goto _test_eof
+       _test_eof29: cs = 29; goto _test_eof
+       _test_eof30: cs = 30; goto _test_eof
+       _test_eof31: cs = 31; goto _test_eof
+       _test_eof32: cs = 32; goto _test_eof
+       _test_eof33: cs = 33; goto _test_eof
+       _test_eof34: cs = 34; goto _test_eof
+       _test_eof35: cs = 35; goto _test_eof
+       _test_eof36: cs = 36; goto _test_eof
+       _test_eof37: cs = 37; goto _test_eof
+       _test_eof38: cs = 38; goto _test_eof
+       _test_eof39: cs = 39; goto _test_eof
+       _test_eof40: cs = 40; goto _test_eof
+       _test_eof41: cs = 41; goto _test_eof
+       _test_eof42: cs = 42; goto _test_eof
+       _test_eof43: cs = 43; goto _test_eof
+       _test_eof44: cs = 44; goto _test_eof
+       _test_eof45: cs = 45; goto _test_eof
+       _test_eof46: cs = 46; goto _test_eof
+       _test_eof47: cs = 47; goto _test_eof
+       _test_eof48: cs = 48; goto _test_eof
+       _test_eof49: cs = 49; goto _test_eof
+       _test_eof50: cs = 50; goto _test_eof
+       _test_eof51: cs = 51; goto _test_eof
+       _test_eof52: cs = 52; goto _test_eof
+       _test_eof53: cs = 53; goto _test_eof
+       _test_eof54: cs = 54; goto _test_eof
+       _test_eof55: cs = 55; goto _test_eof
+       _test_eof56: cs = 56; goto _test_eof
+       _test_eof57: cs = 57; goto _test_eof
 
+       _test_eof: {}
        _out: {}
        }
-#line 109 "http11_parser.rl"
+
+#line 116 "parser.rl"
 
   parser->cs = cs;
   parser->nread += p - (buffer + off);
@@ -1184,38 +1218,32 @@ case 56:
 
   if(parser->body_start) {
     /* final \r\n combo encountered so stop right here */
-    
-#line 1189 "http11_parser.c"
-#line 123 "http11_parser.rl"
     parser->nread++;
   }
 
   return(parser->nread);
 }
 
-int http_parser_finish(http_parser *parser)
+int thin_http_parser_finish(http_parser *parser)
 {
   int cs = parser->cs;
 
-  
-#line 1202 "http11_parser.c"
-#line 134 "http11_parser.rl"
 
   parser->cs = cs;
 
-  if (http_parser_has_error(parser) ) {
+  if (thin_http_parser_has_error(parser) ) {
     return -1;
-  } else if (http_parser_is_finished(parser) ) {
+  } else if (thin_http_parser_is_finished(parser) ) {
     return 1;
   } else {
     return 0;
   }
 }
 
-int http_parser_has_error(http_parser *parser) {
+int thin_http_parser_has_error(http_parser *parser) {
   return parser->cs == http_parser_error;
 }
 
-int http_parser_is_finished(http_parser *parser) {
+int thin_http_parser_is_finished(http_parser *parser) {
   return parser->cs == http_parser_first_final;
 }
@@ -2,7 +2,7 @@
  * Copyright (c) 2005 Zed A. Shaw
  * You can redistribute it and/or modify it under the same terms as Ruby.
  */
-#include "http11_parser.h"
+#include "parser.h"
 #include <stdio.h>
 #include <assert.h>
 #include <stdlib.h>
 
   action start_value { MARK(mark, fpc); }
   action write_value { 
-    if(parser->http_field != NULL) {
+    if (parser->http_field != NULL) {
       parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
     }
   }
   action request_method { 
-    if(parser->request_method != NULL) 
+    if (parser->request_method != NULL) {
       parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
+    }
   }
-  action request_uri { 
-    if(parser->request_uri != NULL)
+  action request_uri {
+    if (parser->request_uri != NULL) {
       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
+    }
   }
   action fragment { 
-    if(parser->fragment != NULL)
+    if (parser->fragment != NULL) {
       parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
+    }
   }
 
   action start_query {MARK(query_start, fpc); }
   action query_string { 
-    if(parser->query_string != NULL)
+    if (parser->query_string != NULL) {
       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
+    }
   }
 
   action http_version {        
-    if(parser->http_version != NULL)
+    if (parser->http_version != NULL) {
       parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
+    }
   }
 
   action request_path {
-    if(parser->request_path != NULL)
+    if (parser->request_path != NULL) {
       parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
+    }
   }
 
   action done { 
     parser->body_start = fpc - buffer + 1; 
-    if(parser->header_done != NULL)
+    if (parser->header_done != NULL) {
       parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
+    }
     fbreak;
   }
 
-  include http_parser_common "http11_parser_common.rl";
+  include http_parser_common "common.rl";
 
 }%%
 
 /** Data **/
 %% write data;
 
-int http_parser_init(http_parser *parser)  {
+int thin_http_parser_init(http_parser *parser)  {
   int cs = 0;
   %% write init;
   parser->cs = cs;
@@ -92,7 +99,7 @@ int http_parser_init(http_parser *parser)  {
 
 
 /** exec **/
-size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
+size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
   const char *p, *pe;
   int cs = parser->cs;
 
@@ -119,34 +126,32 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
 
   if(parser->body_start) {
     /* final \r\n combo encountered so stop right here */
-    %%write eof;
     parser->nread++;
   }
 
   return(parser->nread);
 }
 
-int http_parser_finish(http_parser *parser)
+int thin_http_parser_finish(http_parser *parser)
 {
   int cs = parser->cs;
 
-  %%write eof;
 
   parser->cs = cs;
 
-  if (http_parser_has_error(parser) ) {
+  if (thin_http_parser_has_error(parser) ) {
     return -1;
-  } else if (http_parser_is_finished(parser) ) {
+  } else if (thin_http_parser_is_finished(parser) ) {
     return 1;
   } else {
     return 0;
   }
 }
 
-int http_parser_has_error(http_parser *parser) {
+int thin_http_parser_has_error(http_parser *parser) {
   return parser->cs == http_parser_error;
 }
 
-int http_parser_is_finished(http_parser *parser) {
+int thin_http_parser_is_finished(http_parser *parser) {
   return parser->cs == http_parser_first_final;
 }
@@ -1,21 +1,21 @@
 /**
- * Copyright (c) 2005 Zed A. Shaw
+ * Mongrel Parser adpated to Thin and to play more nicely with Rack specs.
+ * 
+ * Orignal version Copyright (c) 2005 Zed A. Shaw
  * You can redistribute it and/or modify it under the same terms as Ruby.
  */
 #include "ruby.h"
 #include "ext_help.h"
 #include <assert.h>
 #include <string.h>
-#include "http11_parser.h"
+#include "parser.h"
 #include <ctype.h>
 
-static VALUE mMongrel;
+static VALUE mThin;
 static VALUE cHttpParser;
 static VALUE eHttpParserError;
 
-#define id_handler_map rb_intern("@handler_map")
-#define id_http_body rb_intern("@http_body")
-
+static VALUE global_empty;
 static VALUE global_http_prefix;
 static VALUE global_request_method;
 static VALUE global_request_uri;
@@ -34,9 +34,12 @@ static VALUE global_server_port;
 static VALUE global_server_protocol;
 static VALUE global_server_protocol_value;
 static VALUE global_http_host;
-static VALUE global_mongrel_version;
-static VALUE global_server_software;
 static VALUE global_port_80;
+static VALUE global_http_body;
+static VALUE global_url_scheme;
+static VALUE global_url_scheme_value;
+static VALUE global_script_name;
+static VALUE global_path_info;
 
 #define TRIE_INCREASE 30
 
@@ -49,6 +52,15 @@ static VALUE global_port_80;
 /** Defines global strings in the init method. */
 #define DEF_GLOBAL(N, val)   global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
 
+/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#endif
+
+/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(s) (RSTRING(s)->len)
+#endif
 
 /* Defines the maximum allowed lengths for various input elements.*/
 DEF_MAX_LENGTH(FIELD_NAME, 256);
@@ -60,7 +72,7 @@ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
 DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
 
 
-void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
+static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
 {
   char *ch, *end;
   VALUE req = (VALUE)data;
@@ -74,7 +86,7 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
   f = rb_str_dup(global_http_prefix);
   f = rb_str_buf_cat(f, field, flen); 
 
-  for(ch = RSTRING(f)->ptr, end = ch + RSTRING(f)->len; ch < end; ch++) {
+  for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
     if(*ch == '-') {
       *ch = '_';
     } else {
@@ -85,7 +97,7 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
   rb_hash_aset(req, f, v);
 }
 
-void request_method(void *data, const char *at, size_t length)
+static void request_method(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = Qnil;
@@ -94,7 +106,7 @@ void request_method(void *data, const char *at, size_t length)
   rb_hash_aset(req, global_request_method, val);
 }
 
-void request_uri(void *data, const char *at, size_t length)
+static void request_uri(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = Qnil;
@@ -105,7 +117,7 @@ void request_uri(void *data, const char *at, size_t length)
   rb_hash_aset(req, global_request_uri, val);
 }
 
-void fragment(void *data, const char *at, size_t length)
+static void fragment(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = Qnil;
@@ -116,7 +128,7 @@ void fragment(void *data, const char *at, size_t length)
   rb_hash_aset(req, global_fragment, val);
 }
 
-void request_path(void *data, const char *at, size_t length)
+static void request_path(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = Qnil;
@@ -125,9 +137,10 @@ void request_path(void *data, const char *at, size_t length)
 
   val = rb_str_new(at, length);
   rb_hash_aset(req, global_request_path, val);
+  rb_hash_aset(req, global_path_info, val);
 }
 
-void query_string(void *data, const char *at, size_t length)
+static void query_string(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = Qnil;
@@ -138,7 +151,7 @@ void query_string(void *data, const char *at, size_t length)
   rb_hash_aset(req, global_query_string, val);
 }
 
-void http_version(void *data, const char *at, size_t length)
+static void http_version(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = rb_str_new(at, length);
@@ -148,47 +161,64 @@ void http_version(void *data, const char *at, size_t length)
 /** Finalizes the request header to have a bunch of stuff that's
   needed. */
 
-void header_done(void *data, const char *at, size_t length)
+static void header_done(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE temp = Qnil;
   VALUE ctype = Qnil;
   VALUE clen = Qnil;
+  VALUE body = Qnil;
   char *colon = NULL;
 
   clen = rb_hash_aref(req, global_http_content_length);
   if(clen != Qnil) {
     rb_hash_aset(req, global_content_length, clen);
+    rb_hash_delete(req, global_http_content_length);
   }
 
   ctype = rb_hash_aref(req, global_http_content_type);
   if(ctype != Qnil) {
     rb_hash_aset(req, global_content_type, ctype);
+    rb_hash_delete(req, global_http_content_type);
   }
 
   rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
   if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
     /* ruby better close strings off with a '\0' dammit */
-    colon = strchr(RSTRING(temp)->ptr, ':');
+    colon = strchr(RSTRING_PTR(temp), ':');
     if(colon != NULL) {
-      rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING(temp)->ptr));
+      rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
       rb_hash_aset(req, global_server_port, 
-          rb_str_substr(temp, colon - RSTRING(temp)->ptr+1, 
-            RSTRING(temp)->len));
+          rb_str_substr(temp, colon - RSTRING_PTR(temp)+1, 
+            RSTRING_LEN(temp)));
     } else {
       rb_hash_aset(req, global_server_name, temp);
       rb_hash_aset(req, global_server_port, global_port_80);
     }
   }
 
-  /* grab the initial body and stuff it into an ivar */
-  rb_ivar_set(req, id_http_body, rb_str_new(at, length));
+  /* grab the initial body and stuff it into the hash */
+  if(length > 0) {
+    body = rb_hash_aref(req, global_http_body);
+    rb_io_write(body, rb_str_new(at, length));
+  }
+  
+  /* according to Rack specs, query string must be empty string if none */
+  if (rb_hash_aref(req, global_query_string) == Qnil) {
+    rb_hash_aset(req, global_query_string, global_empty);
+  }
+  if (rb_hash_aref(req, global_path_info) == Qnil) {
+    rb_hash_aset(req, global_path_info, global_empty);
+  }
+  
+  /* set some constants */
   rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
-  rb_hash_aset(req, global_server_software, global_mongrel_version);
+  rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
+  rb_hash_aset(req, global_script_name, global_empty);
 }
 
 
-void HttpParser_free(void *data) {
+void Thin_HttpParser_free(void *data) {
   TRACE();
 
   if(data) {
@@ -197,7 +227,7 @@ void HttpParser_free(void *data) {
 }
 
 
-VALUE HttpParser_alloc(VALUE klass)
+VALUE Thin_HttpParser_alloc(VALUE klass)
 {
   VALUE obj;
   http_parser *hp = ALLOC_N(http_parser, 1);
@@ -210,9 +240,9 @@ VALUE HttpParser_alloc(VALUE klass)
   hp->query_string = query_string;
   hp->http_version = http_version;
   hp->header_done = header_done;
-  http_parser_init(hp);
+  thin_http_parser_init(hp);
 
-  obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
+  obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp);
 
   return obj;
 }
@@ -224,11 +254,11 @@ VALUE HttpParser_alloc(VALUE klass)
  *
  * Creates a new parser.
  */
-VALUE HttpParser_init(VALUE self)
+VALUE Thin_HttpParser_init(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
-  http_parser_init(http);
+  thin_http_parser_init(http);
 
   return self;
 }
@@ -241,11 +271,11 @@ VALUE HttpParser_init(VALUE self)
  * Resets the parser to it's initial state so that you can reuse it
  * rather than making new ones.
  */
-VALUE HttpParser_reset(VALUE self)
+VALUE Thin_HttpParser_reset(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
-  http_parser_init(http);
+  thin_http_parser_init(http);
 
   return Qnil;
 }
@@ -258,13 +288,13 @@ VALUE HttpParser_reset(VALUE self)
  * Finishes a parser early which could put in a "good" or bad state.
  * You should call reset after finish it or bad things will happen.
  */
-VALUE HttpParser_finish(VALUE self)
+VALUE Thin_HttpParser_finish(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
-  http_parser_finish(http);
+  thin_http_parser_finish(http);
 
-  return http_parser_is_finished(http) ? Qtrue : Qfalse;
+  return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
 }
 
 
@@ -285,7 +315,7 @@ VALUE HttpParser_finish(VALUE self)
  * the parsing from that position.  It needs all of the original data as well 
  * so you have to append to the data buffer as you read.
  */
-VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
+VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
 {
   http_parser *http = NULL;
   int from = 0;
@@ -295,18 +325,18 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
   DATA_GET(self, http_parser, http);
 
   from = FIX2INT(start);
-  dptr = RSTRING(data)->ptr;
-  dlen = RSTRING(data)->len;
+  dptr = RSTRING_PTR(data);
+  dlen = RSTRING_LEN(data);
 
   if(from >= dlen) {
     rb_raise(eHttpParserError, "Requested start is after data buffer end.");
   } else {
     http->data = (void *)req_hash;
-    http_parser_execute(http, dptr, dlen, from);
+    thin_http_parser_execute(http, dptr, dlen, from);
 
     VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
 
-    if(http_parser_has_error(http)) {
+    if(thin_http_parser_has_error(http)) {
       rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
     } else {
       return INT2FIX(http_parser_nread(http));
@@ -322,12 +352,12 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
  *
  * Tells you whether the parser is in an error state.
  */
-VALUE HttpParser_has_error(VALUE self)
+VALUE Thin_HttpParser_has_error(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
 
-  return http_parser_has_error(http) ? Qtrue : Qfalse;
+  return thin_http_parser_has_error(http) ? Qtrue : Qfalse;
 }
 
 
@@ -337,12 +367,12 @@ VALUE HttpParser_has_error(VALUE self)
  *
  * Tells you whether the parser is finished or not and in a good state.
  */
-VALUE HttpParser_is_finished(VALUE self)
+VALUE Thin_HttpParser_is_finished(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
 
-  return http_parser_is_finished(http) ? Qtrue : Qfalse;
+  return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
 }
 
 
@@ -353,7 +383,7 @@ VALUE HttpParser_is_finished(VALUE self)
  * Returns the amount of data processed so far during this processing cycle.  It is
  * set to 0 on initialize or reset calls and is incremented each time execute is called.
  */
-VALUE HttpParser_nread(VALUE self)
+VALUE Thin_HttpParser_nread(VALUE self)
 {
   http_parser *http = NULL;
   DATA_GET(self, http_parser, http);
@@ -361,11 +391,12 @@ VALUE HttpParser_nread(VALUE self)
   return INT2FIX(http->nread);
 }
 
-void Init_http11()
+void Init_thin_parser()
 {
 
-  mMongrel = rb_define_module("Mongrel");
+  mThin = rb_define_module("Thin");
 
+  DEF_GLOBAL(empty, "");
   DEF_GLOBAL(http_prefix, "HTTP_");
   DEF_GLOBAL(request_method, "REQUEST_METHOD");
   DEF_GLOBAL(request_uri, "REQUEST_URI");
@@ -384,19 +415,22 @@ void Init_http11()
   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
   DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
   DEF_GLOBAL(http_host, "HTTP_HOST");
-  DEF_GLOBAL(mongrel_version, "Mongrel 1.1.5"); /* XXX Why is this defined here? */
-  DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
   DEF_GLOBAL(port_80, "80");
-
-  eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError);
-
-  cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
-  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
-  rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
-  rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
-  rb_define_method(cHttpParser, "finish", HttpParser_finish,0);
-  rb_define_method(cHttpParser, "execute", HttpParser_execute,3);
-  rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
-  rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
-  rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
+  DEF_GLOBAL(http_body, "rack.input");
+  DEF_GLOBAL(url_scheme, "rack.url_scheme");
+  DEF_GLOBAL(url_scheme_value, "http");
+  DEF_GLOBAL(script_name, "SCRIPT_NAME");
+  DEF_GLOBAL(path_info, "PATH_INFO");
+
+  eHttpParserError = rb_define_class_under(mThin, "InvalidRequest", rb_eIOError);
+
+  cHttpParser = rb_define_class_under(mThin, "HttpParser", rb_cObject);
+  rb_define_alloc_func(cHttpParser, Thin_HttpParser_alloc);
+  rb_define_method(cHttpParser, "initialize", Thin_HttpParser_init,0);
+  rb_define_method(cHttpParser, "reset", Thin_HttpParser_reset,0);
+  rb_define_method(cHttpParser, "finish", Thin_HttpParser_finish,0);
+  rb_define_method(cHttpParser, "execute", Thin_HttpParser_execute,3);
+  rb_define_method(cHttpParser, "error?", Thin_HttpParser_has_error,0);
+  rb_define_method(cHttpParser, "finished?", Thin_HttpParser_is_finished,0);
+  rb_define_method(cHttpParser, "nread", Thin_HttpParser_nread,0);
 }
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.8/thin_parser.so b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.8/thin_parser.so
new file mode 100644 (file)
index 0000000..2ab812f
Binary files /dev/null and b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.8/thin_parser.so differ
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.9/thin_parser.so b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.9/thin_parser.so
new file mode 100644 (file)
index 0000000..ef800f9
Binary files /dev/null and b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/1.9/thin_parser.so differ
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/loader.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/loader.rb
new file mode 100644 (file)
index 0000000..c36ad30
--- /dev/null
@@ -0,0 +1,91 @@
+module Rack
+  class AdapterNotFound < RuntimeError; end
+  
+  # Mapping used to guess which adapter to use in <tt>Adapter.for</tt>.
+  # Framework <name> => <file unique to this framework> in order they will
+  # be tested.
+  # +nil+ for value to never guess.
+  # NOTE: If a framework has a file that is not unique, make sure to place
+  # it at the end.
+  ADAPTERS = [
+    [:rack,    'config.ru'],
+    [:rails,   'config/environment.rb'],
+    [:ramaze,  'start.rb'],
+    [:halcyon, 'runner.ru'],
+    [:merb,    'config/init.rb'],
+    [:mack,    'config/app_config/default.yml'],
+    [:mack,    'config/configatron/default.rb'],
+    [:file,    nil]
+  ]
+  
+  module Adapter
+    # Guess which adapter to use based on the directory structure
+    # or file content.
+    # Returns a symbol representing the name of the adapter to use
+    # to load the application under <tt>dir/</tt>.
+    def self.guess(dir)
+      ADAPTERS.each do |adapter, file|
+        return adapter if file && ::File.exist?(::File.join(dir, file))
+      end
+      raise AdapterNotFound, "No adapter found for #{dir}"
+    end
+    
+    # Load a Rack application from a Rack config file (.ru).
+    def self.load(config)
+      rackup_code = ::File.read(config)
+      eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, config)
+    end
+    
+    # Loads an adapter identified by +name+ using +options+ hash.
+    def self.for(name, options={})
+      ENV['RACK_ENV'] = options[:environment]
+      
+      case name.to_sym
+      when :rack
+        return load(::File.join(options[:chdir], "config.ru"))
+        
+      when :rails
+        return Rails.new(options.merge(:root => options[:chdir]))
+      
+      when :ramaze
+        require "#{options[:chdir]}/start"
+        
+        Ramaze.trait[:essentials].delete Ramaze::Adapter
+        Ramaze.start :force => true
+        
+        return Ramaze::Adapter::Base
+        
+      when :merb
+        require 'merb-core'
+        
+        Merb::Config.setup(:merb_root   => options[:chdir],
+                           :environment => options[:environment])
+        Merb.environment = Merb::Config[:environment]
+        Merb.root = Merb::Config[:merb_root]
+        Merb::BootLoader.run
+        
+        return Merb::Rack::Application.new
+        
+      when :halcyon
+        require 'halcyon'
+        
+        $:.unshift(Halcyon.root/'lib')
+        
+        return Halcyon::Runner.new
+        
+      when :mack
+        ENV["MACK_ENV"] = options[:environment]
+        load(::File.join(options[:chdir], "Rakefile"))
+        require 'mack'
+        return Mack::Utils::Server.build_app
+        
+      when :file
+        return Rack::File.new(options[:chdir])
+        
+      else
+        raise AdapterNotFound, "Adapter not found: #{name}"
+        
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/rails.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/rack/adapter/rails.rb
new file mode 100644 (file)
index 0000000..4fec0e6
--- /dev/null
@@ -0,0 +1,183 @@
+require 'cgi'
+
+# Adapter to run a Rails app with any supported Rack handler.
+# By default it will try to load the Rails application in the
+# current directory in the development environment.
+#
+# Options:
+#  root: Root directory of the Rails app
+#  environment: Rails environment to run in (development [default], production or test)
+#  prefix: Set the relative URL root.
+#
+# Based on http://fuzed.rubyforge.org/ Rails adapter
+module Rack
+  module Adapter 
+    class Rails
+      FILE_METHODS = %w(GET HEAD).freeze
+      
+      def initialize(options={})
+        @root   = options[:root]         || Dir.pwd
+        @env    = options[:environment]  || 'development'
+        @prefix = options[:prefix]
+        
+        load_application
+        
+        @rails_app = if self.class.rack_based?
+          ActionController::Dispatcher.new
+        else
+          CgiApp.new
+        end
+        
+        @file_app = Rack::File.new(::File.join(RAILS_ROOT, "public"))
+      end
+      
+      def load_application
+        ENV['RAILS_ENV'] = @env
+
+        require "#{@root}/config/environment"
+        require 'dispatcher'
+        
+        if @prefix
+          if ActionController::Base.respond_to?(:relative_url_root=)
+            ActionController::Base.relative_url_root = @prefix # Rails 2.1.1
+          else
+            ActionController::AbstractRequest.relative_url_root = @prefix
+          end
+        end
+      end
+      
+      def file_exist?(path)
+        full_path = ::File.join(@file_app.root, Utils.unescape(path))
+        ::File.file?(full_path) && ::File.readable_real?(full_path)
+      end
+      
+      def call(env)
+        path        = env['PATH_INFO'].chomp('/')
+        method      = env['REQUEST_METHOD']
+        cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
+        
+        if FILE_METHODS.include?(method)
+          if file_exist?(path)              # Serve the file if it's there
+            return @file_app.call(env)
+          elsif file_exist?(cached_path)    # Serve the page cache if it's there
+            env['PATH_INFO'] = cached_path
+            return @file_app.call(env)
+          end
+        end
+        
+        # No static file, let Rails handle it
+        @rails_app.call(env)
+      end
+      
+      def self.rack_based?
+        rails_version = ::Rails::VERSION
+        return false if rails_version::MAJOR < 2
+        return false if rails_version::MAJOR == 2 && rails_version::MINOR < 2
+        return false if rails_version::MAJOR == 2 && rails_version::MINOR == 2 && rails_version::TINY < 3
+        true # >= 2.2.3
+      end
+    
+      protected
+        # For Rails pre Rack (2.3)
+        class CgiApp
+          def call(env)
+            request         = Request.new(env)
+            response        = Response.new
+            session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
+            cgi             = CGIWrapper.new(request, response)
+
+            Dispatcher.dispatch(cgi, session_options, response)
+
+            response.finish
+          end
+        end
+        
+        class CGIWrapper < ::CGI
+          def initialize(request, response, *args)
+            @request  = request
+            @response = response
+            @args     = *args
+            @input    = request.body
+
+            super *args
+          end
+        
+          def header(options = "text/html")
+            if options.is_a?(String)
+              @response['Content-Type']     = options unless @response['Content-Type']
+            else
+              @response['Content-Length']   = options.delete('Content-Length').to_s if options['Content-Length']
+            
+              @response['Content-Type']     = options.delete('type') || "text/html"
+              @response['Content-Type']    += "; charset=" + options.delete('charset') if options['charset']
+                        
+              @response['Content-Language'] = options.delete('language') if options['language']
+              @response['Expires']          = options.delete('expires') if options['expires']
+
+              @response.status              = options.delete('Status') if options['Status']
+              
+              # Convert 'cookie' header to 'Set-Cookie' headers.
+              # Because Set-Cookie header can appear more the once in the response body, 
+              # we store it in a line break seperated string that will be translated to
+              # multiple Set-Cookie header by the handler.
+              if cookie = options.delete('cookie')
+                cookies = []
+                
+                case cookie
+                  when Array then cookie.each { |c| cookies << c.to_s }
+                  when Hash  then cookie.each { |_, c| cookies << c.to_s }
+                  else            cookies << cookie.to_s
+                end
+                
+                @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
+                
+                @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact
+                # See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69
+                if Thin.ruby_18?
+                  @response['Set-Cookie'].flatten!
+                else
+                  @response['Set-Cookie'] = @response['Set-Cookie'].join("\n")
+                end
+              end
+              
+              options.each { |k,v| @response[k] = v }
+            end
+            
+            ""
+          end
+                        
+          def params
+            @params ||= @request.params
+          end
+        
+          def cookies
+            @request.cookies
+          end
+        
+          def query_string
+            @request.query_string
+          end
+          
+          # Used to wrap the normal args variable used inside CGI.
+          def args
+            @args
+          end
+    
+          # Used to wrap the normal env_table variable used inside CGI.
+          def env_table
+            @request.env
+          end
+    
+          # Used to wrap the normal stdinput variable used inside CGI.
+          def stdinput
+            @input
+          end
+        
+          def stdoutput
+            STDERR.puts "stdoutput should not be used."
+            @response.body
+          end
+      end
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin.rb
new file mode 100644 (file)
index 0000000..10b5ff3
--- /dev/null
@@ -0,0 +1,54 @@
+require 'fileutils'
+require 'timeout'
+require 'stringio'
+require 'time'
+require 'forwardable'
+require 'openssl'
+require 'eventmachine'
+require 'rack'
+
+module Thin
+  ROOT = File.expand_path(File.dirname(__FILE__))
+  
+  autoload :Command,            "#{ROOT}/thin/command"
+  autoload :Connection,         "#{ROOT}/thin/connection"
+  autoload :Daemonizable,       "#{ROOT}/thin/daemonizing"
+  autoload :Logging,            "#{ROOT}/thin/logging"
+  autoload :Headers,            "#{ROOT}/thin/headers"
+  autoload :Request,            "#{ROOT}/thin/request"
+  autoload :Response,           "#{ROOT}/thin/response"
+  autoload :Runner,             "#{ROOT}/thin/runner"
+  autoload :Server,             "#{ROOT}/thin/server"
+  autoload :Stats,              "#{ROOT}/thin/stats"
+  
+  module Backends
+    autoload :Base,             "#{ROOT}/thin/backends/base"
+    autoload :SwiftiplyClient,  "#{ROOT}/thin/backends/swiftiply_client"
+    autoload :TcpServer,        "#{ROOT}/thin/backends/tcp_server"
+    autoload :UnixServer,       "#{ROOT}/thin/backends/unix_server"
+  end
+  
+  module Controllers
+    autoload :Cluster,          "#{ROOT}/thin/controllers/cluster"
+    autoload :Controller,       "#{ROOT}/thin/controllers/controller"
+    autoload :Service,          "#{ROOT}/thin/controllers/service"
+  end
+end
+
+require "#{Thin::ROOT}/thin/version"
+require "#{Thin::ROOT}/thin/statuses"
+require "#{Thin::ROOT}/rack/adapter/loader"
+
+if Thin.win?
+  # Select proper binary under Windows
+  major_ruby_version = RUBY_VERSION[/^(\d+\.\d+)/]
+  require "#{Thin::ROOT}/#{major_ruby_version}/thin_parser"
+else
+  require "#{Thin::ROOT}/thin_parser"
+end
+
+module Rack
+  module Adapter
+    autoload :Rails, "#{Thin::ROOT}/rack/adapter/rails"
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/base.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/base.rb
new file mode 100644 (file)
index 0000000..c620af8
--- /dev/null
@@ -0,0 +1,149 @@
+module Thin
+  module Backends
+    # A Backend connects the server to the client. It handles:
+    # * connection/disconnection to the server
+    # * initialization of the connections
+    # * manitoring of the active connections.
+    #
+    # == Implementing your own backend
+    # You can create your own minimal backend by inheriting this class and
+    # defining the +connect+ and +disconnect+ method.
+    # If your backend is not based on EventMachine you also need to redefine
+    # the +start+, +stop+, <tt>stop!</tt> and +config+ methods.
+    class Base
+      # Server serving the connections throught the backend
+      attr_accessor :server
+      
+      # Maximum time for incoming data to arrive
+      attr_accessor :timeout
+      
+      # Maximum number of file or socket descriptors that the server may open.
+      attr_accessor :maximum_connections
+      
+      # Maximum number of connections that can be persistent
+      attr_accessor :maximum_persistent_connections
+      
+      # Allow using threads in the backend.
+      attr_writer :threaded
+      def threaded?; @threaded end
+      
+      # Allow using SSL in the backend.
+      attr_writer :ssl, :ssl_options
+      def ssl?; @ssl end
+      
+      # Number of persistent connections currently opened
+      attr_accessor :persistent_connection_count
+      
+      # Disable the use of epoll under Linux
+      attr_accessor :no_epoll
+      
+      def initialize
+        @connections                    = []
+        @timeout                        = Server::DEFAULT_TIMEOUT
+        @persistent_connection_count    = 0
+        @maximum_connections            = Server::DEFAULT_MAXIMUM_CONNECTIONS
+        @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
+        @no_epoll                       = false
+      end
+      
+      # Start the backend and connect it.
+      def start
+        @stopping = false
+        starter   = proc do
+          connect
+          @running = true
+        end
+        
+        # Allow for early run up of eventmachine.
+        if EventMachine.reactor_running?
+          starter.call
+        else
+          EventMachine.run(&starter)
+        end
+      end
+      
+      # Stop of the backend from accepting new connections.
+      def stop
+        @running  = false
+        @stopping = true
+        
+        # Do not accept anymore connection
+        disconnect
+        stop! if @connections.empty?
+      end
+      
+      # Force stop of the backend NOW, too bad for the current connections.
+      def stop!
+        @running  = false
+        @stopping = false
+        
+        EventMachine.stop if EventMachine.reactor_running?
+        @connections.each { |connection| connection.close_connection }
+        close
+      end
+      
+      # Configure the backend. This method will be called before droping superuser privileges,
+      # so you can do crazy stuff that require godlike powers here.
+      def config
+        # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
+        EventMachine.epoll unless @no_epoll
+        
+        # Set the maximum number of socket descriptors that the server may open.
+        # The process needs to have required privilege to set it higher the 1024 on
+        # some systems.
+        @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
+      end
+      
+      # Free up resources used by the backend.
+      def close
+      end
+      
+      # Returns +true+ if the backend is connected and running.
+      def running?
+        @running
+      end
+            
+      # Called by a connection when it's unbinded.
+      def connection_finished(connection)
+        @persistent_connection_count -= 1 if connection.can_persist?
+        @connections.delete(connection)
+        
+        # Finalize gracefull stop if there's no more active connection.
+        stop! if @stopping && @connections.empty?
+      end
+      
+      # Returns +true+ if no active connection.
+      def empty?
+        @connections.empty?
+      end
+      
+      # Number of active connections.
+      def size
+        @connections.size
+      end
+      
+      protected
+        # Initialize a new connection to a client.
+        def initialize_connection(connection)
+          connection.backend                 = self
+          connection.app                     = @server.app
+          connection.comm_inactivity_timeout = @timeout
+          connection.threaded                = @threaded
+          
+          if @ssl
+            connection.start_tls(@ssl_options)
+          end
+
+          # We control the number of persistent connections by keeping
+          # a count of the total one allowed yet.
+          if @persistent_connection_count < @maximum_persistent_connections
+            connection.can_persist!
+            @persistent_connection_count += 1
+          end
+
+          @connections << connection
+        end
+      
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/swiftiply_client.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/swiftiply_client.rb
new file mode 100644 (file)
index 0000000..506a84c
--- /dev/null
@@ -0,0 +1,56 @@
+module Thin
+  module Backends
+    # Backend to act as a Swiftiply client (http://swiftiply.swiftcore.org).
+    class SwiftiplyClient < Base
+      attr_accessor :key
+      
+      attr_accessor :host, :port
+      
+      def initialize(host, port, options={})
+        @host = host
+        @port = port.to_i
+        @key  = options[:swiftiply].to_s
+        super()
+      end
+
+      # Connect the server
+      def connect
+        EventMachine.connect(@host, @port, SwiftiplyConnection, &method(:initialize_connection))
+      end
+
+      # Stops the server
+      def disconnect
+        EventMachine.stop
+      end
+
+      def to_s
+        "#{@host}:#{@port} swiftiply"
+      end
+    end    
+  end
+
+  class SwiftiplyConnection < Connection
+    def connection_completed
+      send_data swiftiply_handshake(@backend.key)
+    end
+    
+    def persistent?
+      true
+    end
+    
+    def unbind
+      super
+      EventMachine.add_timer(rand(2)) { reconnect(@backend.host, @backend.port) } if @backend.running?
+    end
+    
+    protected
+      def swiftiply_handshake(key)
+        'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @backend.port) << sprintf('%02x', key.length) << key
+      end
+      
+      # For some reason Swiftiply request the current host
+      def host_ip
+        Socket.gethostbyname(@backend.host)[3].unpack('CCCC') rescue [0,0,0,0]
+      end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/tcp_server.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/tcp_server.rb
new file mode 100644 (file)
index 0000000..0f2b38e
--- /dev/null
@@ -0,0 +1,29 @@
+module Thin
+  module Backends
+    # Backend to act as a TCP socket server.
+    class TcpServer < Base
+      # Address and port on which the server is listening for connections.
+      attr_accessor :host, :port
+      
+      def initialize(host, port)
+        @host = host
+        @port = port
+        super()
+      end
+      
+      # Connect the server
+      def connect
+        @signature = EventMachine.start_server(@host, @port, Connection, &method(:initialize_connection))
+      end
+      
+      # Stops the server
+      def disconnect
+        EventMachine.stop_server(@signature)
+      end
+            
+      def to_s
+        "#{@host}:#{@port}"
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/unix_server.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/backends/unix_server.rb
new file mode 100644 (file)
index 0000000..1f5e491
--- /dev/null
@@ -0,0 +1,56 @@
+module Thin
+  module Backends
+    # Backend to act as a UNIX domain socket server.
+    class UnixServer < Base
+      # UNIX domain socket on which the server is listening for connections.
+      attr_accessor :socket
+      
+      def initialize(socket)
+        raise PlatformNotSupported, 'UNIX domain sockets not available on Windows' if Thin.win?
+        @socket = socket
+        super()
+      end
+      
+      # Connect the server
+      def connect
+        at_exit { remove_socket_file } # In case it crashes
+        old_umask = File.umask(0)
+        begin
+          EventMachine.start_unix_domain_server(@socket, UnixConnection, &method(:initialize_connection))
+          # HACK EventMachine.start_unix_domain_server doesn't return the connection signature
+          #      so we have to go in the internal stuff to find it.
+        @signature = EventMachine.instance_eval{@acceptors.keys.first}
+        ensure
+          File.umask(old_umask)
+        end
+      end
+      
+      # Stops the server
+      def disconnect
+        EventMachine.stop_server(@signature)
+      end
+      
+      # Free up resources used by the backend.
+      def close
+        remove_socket_file
+      end
+      
+      def to_s
+        @socket
+      end
+      
+      protected
+        def remove_socket_file
+          File.delete(@socket) if @socket && File.exist?(@socket)
+        end
+    end    
+  end
+
+  # Connection through a UNIX domain socket.
+  class UnixConnection < Connection
+    protected
+      def socket_address        
+        '127.0.0.1' # Unix domain sockets can only be local
+      end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/command.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/command.rb
new file mode 100644 (file)
index 0000000..798b1fc
--- /dev/null
@@ -0,0 +1,53 @@
+require 'open3'
+
+module Thin
+  # Run a command through the +thin+ command-line script.
+  class Command
+    include Logging
+    
+    class << self
+      # Path to the +thin+ script used to control the servers.
+      # Leave this to default to use the one in the path.
+      attr_accessor :script
+    end
+    
+    def initialize(name, options={})
+      @name    = name
+      @options = options
+    end
+    
+    def self.run(*args)
+      new(*args).run
+    end
+    
+    # Send the command to the +thin+ script
+    def run
+      shell_cmd = shellify
+      trace shell_cmd
+      trap('INT') {} # Ignore INT signal to pass CTRL+C to subprocess
+      Open3.popen3(shell_cmd) do |stdin, stdout, stderr|
+        log stdout.gets until stdout.eof?
+        log stderr.gets until stderr.eof?
+      end
+    end
+    
+    # Turn into a runnable shell command
+    def shellify
+      shellified_options = @options.inject([]) do |args, (name, value)|
+        option_name = name.to_s.tr("_", "-")
+        case value
+        when NilClass,
+             TrueClass then args << "--#{option_name}"
+        when FalseClass
+        when Array     then value.each { |v| args << "--#{option_name}=#{v.inspect}" }
+        else                args << "--#{option_name}=#{value.inspect}"
+        end
+        args
+      end
+      
+      raise ArgumentError, "Path to thin script can't be found, set Command.script" unless self.class.script
+      
+      "#{self.class.script} #{@name} #{shellified_options.compact.join(' ')}"
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/connection.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/connection.rb
new file mode 100644 (file)
index 0000000..e9d1a4f
--- /dev/null
@@ -0,0 +1,230 @@
+require 'socket'
+
+module Thin
+  # Connection between the server and client.
+  # This class is instanciated by EventMachine on each new connection
+  # that is opened.
+  class Connection < EventMachine::Connection
+    CONTENT_LENGTH    = 'Content-Length'.freeze
+    TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
+    CHUNKED_REGEXP    = /\bchunked\b/i.freeze
+
+    include Logging
+    
+    # This is a template async response. N.B. Can't use string for body on 1.9
+    AsyncResponse = [-1, {}, []].freeze
+    
+    # Rack application (adapter) served by this connection.
+    attr_accessor :app
+
+    # Backend to the server
+    attr_accessor :backend
+
+    # Current request served by the connection
+    attr_accessor :request
+
+    # Next response sent through the connection
+    attr_accessor :response
+
+    # Calling the application in a threaded allowing
+    # concurrent processing of requests.
+    attr_writer :threaded
+    
+    # Get the connection ready to process a request.
+    def post_init
+      @request  = Request.new
+      @response = Response.new
+    end
+
+    # Called when data is received from the client.
+    def receive_data(data)
+      trace { data }
+      process if @request.parse(data)
+    rescue InvalidRequest => e
+      log "!! Invalid request"
+      log_error e
+      close_connection
+    end
+
+    # Called when all data was received and the request
+    # is ready to be processed.
+    def process
+      if threaded?
+        @request.threaded = true
+        EventMachine.defer(method(:pre_process), method(:post_process))
+      else
+        @request.threaded = false
+        post_process(pre_process)
+      end
+    end
+
+    def pre_process
+      # Add client info to the request env
+      @request.remote_address = remote_address
+
+      # Connection may be closed unless the App#call response was a [-1, ...]
+      # It should be noted that connection objects will linger until this 
+      # callback is no longer referenced, so be tidy!
+      @request.async_callback = method(:post_process)
+      
+      if @backend.ssl?
+        @request.env["rack.url_scheme"] = "https"
+        
+        if cert = get_peer_cert
+          @request.env['rack.peer_cert'] = cert
+        end
+      end
+
+      # When we're under a non-async framework like rails, we can still spawn
+      # off async responses using the callback info, so there's little point
+      # in removing this.
+      response = AsyncResponse
+      catch(:async) do
+        # Process the request calling the Rack adapter
+        response = @app.call(@request.env)
+      end
+      response
+    rescue Exception
+      handle_error
+      terminate_request
+      nil # Signal to post_process that the request could not be processed
+    end
+
+    def post_process(result)
+      return unless result
+      result = result.to_a
+      
+      # Status code -1 indicates that we're going to respond later (async).
+      return if result.first == AsyncResponse.first
+
+      # Set the Content-Length header if possible
+      set_content_length(result) if need_content_length?(result)
+      
+      @response.status, @response.headers, @response.body = *result
+
+      log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?
+
+      # Make the response persistent if requested by the client
+      @response.persistent! if @request.persistent?
+
+      # Send the response
+      @response.each do |chunk|
+        trace { chunk }
+        send_data chunk
+      end
+
+    rescue Exception
+      handle_error
+    ensure
+      # If the body is being deferred, then terminate afterward.
+      if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
+        @response.body.callback { terminate_request }
+        @response.body.errback  { terminate_request }
+      else
+        # Don't terminate the response if we're going async.
+        terminate_request unless result && result.first == AsyncResponse.first
+      end
+    end
+
+    # Logs catched exception and closes the connection.
+    def handle_error
+      log "!! Unexpected error while processing request: #{$!.message}"
+      log_error
+      close_connection rescue nil
+    end
+
+    def close_request_response
+      @request.async_close.succeed if @request.async_close
+      @request.close  rescue nil
+      @response.close rescue nil
+    end
+
+    # Does request and response cleanup (closes open IO streams and
+    # deletes created temporary files).
+    # Re-initializes response and request if client supports persistent
+    # connection.
+    def terminate_request
+      unless persistent?
+        close_connection_after_writing rescue nil
+        close_request_response
+      else
+        close_request_response
+        # Prepare the connection for another request if the client
+        # supports HTTP pipelining (persistent connection).
+        post_init
+      end
+    end
+
+    # Called when the connection is unbinded from the socket
+    # and can no longer be used to process requests.
+    def unbind
+      @request.async_close.succeed if @request.async_close
+      @response.body.fail if @response.body.respond_to?(:fail)
+      @backend.connection_finished(self)
+    end
+
+    # Allows this connection to be persistent.
+    def can_persist!
+      @can_persist = true
+    end
+
+    # Return +true+ if this connection is allowed to stay open and be persistent.
+    def can_persist?
+      @can_persist
+    end
+
+    # Return +true+ if the connection must be left open
+    # and ready to be reused for another request.
+    def persistent?
+      @can_persist && @response.persistent?
+    end
+
+    # +true+ if <tt>app.call</tt> will be called inside a thread.
+    # You can set all requests as threaded setting <tt>Connection#threaded=true</tt>
+    # or on a per-request case returning +true+ in <tt>app.deferred?</tt>.
+    def threaded?
+      @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
+    end
+
+    # IP Address of the remote client.
+    def remote_address
+      socket_address
+    rescue Exception
+      log_error
+      nil
+    end
+
+    protected
+
+      # Returns IP address of peer as a string.
+      def socket_address
+        Socket.unpack_sockaddr_in(get_peername)[1]
+      end
+
+    private
+      def need_content_length?(result)
+        status, headers, body = result
+        return false if status == -1
+        return false if headers.has_key?(CONTENT_LENGTH)
+        return false if (100..199).include?(status) || status == 204 || status == 304
+        return false if headers.has_key?(TRANSFER_ENCODING) && headers[TRANSFER_ENCODING] =~ CHUNKED_REGEXP
+        return false unless body.kind_of?(String) || body.kind_of?(Array)
+        true
+      end
+
+      def set_content_length(result)
+        headers, body = result[1..2]
+        case body
+        when String
+          # See http://redmine.ruby-lang.org/issues/show/203
+          headers[CONTENT_LENGTH] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
+        when Array
+           bytes = 0
+           body.each do |p|
+             bytes += p.respond_to?(:bytesize) ? p.bytesize : p.size
+           end
+           headers[CONTENT_LENGTH] = bytes.to_s
+        end
+      end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/cluster.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/cluster.rb
new file mode 100644 (file)
index 0000000..f8749f0
--- /dev/null
@@ -0,0 +1,178 @@
+require 'socket'
+
+module Thin
+  # An exception class to handle the event that server didn't start on time
+  class RestartTimeout < RuntimeError; end
+  
+  module Controllers
+    # Control a set of servers.
+    # * Generate start and stop commands and run them.
+    # * Inject the port or socket number in the pid and log filenames.
+    # Servers are started throught the +thin+ command-line script.
+    class Cluster < Controller
+      # Cluster only options that should not be passed in the command sent
+      # to the indiviual servers.
+      CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
+      
+      # Maximum wait time for the server to be restarted
+      DEFAULT_WAIT_TIME = 30    # seconds
+      
+      # Create a new cluster of servers launched using +options+.
+      def initialize(options)
+        super
+        # Cluster can only contain daemonized servers
+        @options.merge!(:daemonize => true)
+      end
+      
+      def first_port; @options[:port]     end
+      def address;    @options[:address]  end
+      def socket;     @options[:socket]   end
+      def pid_file;   @options[:pid]      end
+      def log_file;   @options[:log]      end
+      def size;       @options[:servers]  end
+      def only;       @options[:only]     end
+      def onebyone;   @options[:onebyone] end
+      def wait;       @options[:wait]     end
+      
+      def swiftiply?
+        @options.has_key?(:swiftiply)
+      end
+    
+      # Start the servers
+      def start
+        with_each_server { |n| start_server n }
+      end
+    
+      # Start a single server
+      def start_server(number)
+        log "Starting server on #{server_id(number)} ... "
+      
+        run :start, number
+      end
+  
+      # Stop the servers
+      def stop
+        with_each_server { |n| stop_server n }
+      end
+    
+      # Stop a single server
+      def stop_server(number)
+        log "Stopping server on #{server_id(number)} ... "
+      
+        run :stop, number
+      end
+    
+      # Stop and start the servers.
+      def restart
+        unless onebyone
+          # Let's do a normal restart by defaults
+          stop
+          sleep 0.1 # Let's breath a bit shall we ?
+          start
+        else
+          with_each_server do |n| 
+            stop_server(n)
+            sleep 0.1 # Let's breath a bit shall we ?
+            start_server(n)
+            wait_until_server_started(n)
+          end
+        end
+      end
+      
+      def test_socket(number)
+        if socket
+          UNIXSocket.new(socket_for(number))
+        else
+          TCPSocket.new(address, number)
+        end
+      rescue
+        nil
+      end
+      
+      # Make sure the server is running before moving on to the next one.
+      def wait_until_server_started(number)
+        log "Waiting for server to start ..."
+        STDOUT.flush # Need this to make sure user got the message
+        
+        tries = 0
+        loop do
+          if test_socket = test_socket(number)
+            test_socket.close
+            break
+          elsif tries < wait
+            sleep 1
+            tries += 1
+          else
+            raise RestartTimeout, "The server didn't start in time. Please look at server's log file " +
+                                  "for more information, or set the value of 'wait' in your config " +
+                                  "file to be higher (defaults: 30)."
+          end
+        end
+      end
+    
+      def server_id(number)
+        if socket
+          socket_for(number)
+        elsif swiftiply?
+          [address, first_port, number].join(':')
+        else
+          [address, number].join(':')
+        end
+      end
+    
+      def log_file_for(number)
+        include_server_number log_file, number
+      end
+    
+      def pid_file_for(number)
+        include_server_number pid_file, number
+      end
+    
+      def socket_for(number)
+        include_server_number socket, number
+      end
+    
+      def pid_for(number)
+        File.read(pid_file_for(number)).chomp.to_i
+      end
+      
+      private
+        # Send the command to the +thin+ script
+        def run(cmd, number)
+          cmd_options = @options.reject { |option, value| CLUSTER_OPTIONS.include?(option) }
+          cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
+          if socket
+            cmd_options.merge!(:socket => socket_for(number))
+          elsif swiftiply?
+            cmd_options.merge!(:port => first_port)
+          else
+            cmd_options.merge!(:port => number)
+          end
+          Command.run(cmd, cmd_options)
+        end
+      
+        def with_each_server
+          if only
+            if first_port && only < 80
+              # interpret +only+ as a sequence number
+              yield first_port + only
+            else
+              # interpret +only+ as an absolute port number
+              yield only
+            end
+          elsif socket || swiftiply?
+            size.times { |n| yield n }
+          else
+            size.times { |n| yield first_port + n }
+          end
+        end
+      
+        # Add the server port or number in the filename
+        # so each instance get its own file
+        def include_server_number(path, number)
+          ext = File.extname(path)
+          path.gsub(/#{ext}$/, ".#{number}#{ext}")
+        end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/controller.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/controller.rb
new file mode 100644 (file)
index 0000000..dfb546c
--- /dev/null
@@ -0,0 +1,188 @@
+require 'yaml'
+
+module Thin
+  # Error raised that will abort the process and print not backtrace.
+  class RunnerError < RuntimeError; end
+  
+  # Raised when a mandatory option is missing to run a command.
+  class OptionRequired < RunnerError
+    def initialize(option)
+      super("#{option} option required")
+    end
+  end
+  
+  # Raised when an option is not valid.
+  class InvalidOption < RunnerError; end
+  
+  # Build and control Thin servers.
+  # Hey Controller pattern is not only for web apps yo!
+  module Controllers  
+    # Controls one Thin server.
+    # Allow to start, stop, restart and configure a single thin server.
+    class Controller
+      include Logging
+    
+      # Command line options passed to the thin script
+      attr_accessor :options
+    
+      def initialize(options)
+        @options = options
+        
+        if @options[:socket]
+          @options.delete(:address)
+          @options.delete(:port)
+        end
+      end
+    
+      def start
+        # Constantize backend class
+        @options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend]
+        
+        server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket
+                            @options[:port],                         # Port ignored on UNIX socket
+                            @options)
+        
+        # Set options
+        server.pid_file                       = @options[:pid]
+        server.log_file                       = @options[:log]
+        server.timeout                        = @options[:timeout]
+        server.maximum_connections            = @options[:max_conns]
+        server.maximum_persistent_connections = @options[:max_persistent_conns]
+        server.threaded                       = @options[:threaded]
+        server.no_epoll                       = @options[:no_epoll] if server.backend.respond_to?(:no_epoll=)
+
+        # ssl support
+        if @options[:ssl]
+          server.ssl = true
+          server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => @options[:ssl_verify] }
+        end
+
+        # Detach the process, after this line the current process returns
+        server.daemonize if @options[:daemonize]
+
+        # +config+ must be called before changing privileges since it might require superuser power.
+        server.config
+        
+        server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]
+
+        # If a Rack config file is specified we eval it inside a Rack::Builder block to create
+        # a Rack adapter from it. Or else we guess which adapter to use and load it.
+        if @options[:rackup]
+          server.app = load_rackup_config
+        else
+          server.app = load_adapter
+        end
+
+        # If a prefix is required, wrap in Rack URL mapper
+        server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix]
+
+        # If a stats URL is specified, wrap in Stats adapter
+        server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats]
+
+        # Register restart procedure which just start another process with same options,
+        # so that's why this is done here.
+        server.on_restart { Command.run(:start, @options) }
+
+        server.start
+      end
+    
+      def stop
+        raise OptionRequired, :pid unless @options[:pid]
+      
+        tail_log(@options[:log]) do
+          if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60))
+            wait_for_file :deletion, @options[:pid]
+          end
+        end
+      end
+    
+      def restart
+        raise OptionRequired, :pid unless @options[:pid]
+        
+        tail_log(@options[:log]) do
+          if Server.restart(@options[:pid])
+            wait_for_file :creation, @options[:pid]
+          end
+        end
+      end
+    
+      def config
+        config_file = @options.delete(:config) || raise(OptionRequired, :config)
+
+        # Stringify keys
+        @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }
+
+        File.open(config_file, 'w') { |f| f << @options.to_yaml }
+        log ">> Wrote configuration to #{config_file}"
+      end
+      
+      protected
+        # Wait for a pid file to either be created or deleted.
+        def wait_for_file(state, file)
+          Timeout.timeout(@options[:timeout] || 30) do
+            case state
+            when :creation then sleep 0.1 until File.exist?(file)
+            when :deletion then sleep 0.1 while File.exist?(file)
+            end
+          end
+        end
+        
+        # Tail the log file of server +number+ during the execution of the block.        
+        def tail_log(log_file)
+          if log_file
+            tail_thread = tail(log_file)
+            yield
+            tail_thread.kill
+          else
+            yield
+          end
+        end
+        
+        # Acts like GNU tail command. Taken from Rails.
+        def tail(file)
+          cursor = File.exist?(file) ? File.size(file) : 0
+          last_checked = Time.now
+          tail_thread = Thread.new do
+            Thread.pass until File.exist?(file)
+            File.open(file, 'r') do |f|
+              loop do
+                f.seek cursor
+                if f.mtime > last_checked
+                  last_checked = f.mtime
+                  contents = f.read
+                  cursor += contents.length
+                  print contents
+                  STDOUT.flush
+                end
+                sleep 0.1
+              end
+            end
+          end
+          sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file
+          tail_thread
+        end
+
+      private
+        def load_adapter
+          adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir])
+          log ">> Using #{adapter} adapter"
+          Rack::Adapter.for(adapter, @options)
+        rescue Rack::AdapterNotFound => e
+          raise InvalidOption, e.message
+        end
+        
+        def load_rackup_config
+          ENV['RACK_ENV'] = @options[:environment]
+          case @options[:rackup]
+          when /\.rb$/
+            Kernel.load(@options[:rackup])
+            Object.const_get(File.basename(@options[:rackup], '.rb').capitalize.to_sym)
+          when /\.ru$/
+            Rack::Adapter.load(@options[:rackup])
+          else
+            raise "Invalid rackup file.  please specify either a .ru or .rb file"
+          end
+        end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.rb
new file mode 100644 (file)
index 0000000..540e659
--- /dev/null
@@ -0,0 +1,75 @@
+require 'erb'
+
+module Thin
+  module Controllers
+    # System service controller to launch all servers which
+    # config files are in a directory.
+    class Service < Controller
+      INITD_PATH          = '/etc/init.d/thin'
+      DEFAULT_CONFIG_PATH = '/etc/thin'
+      TEMPLATE            = File.dirname(__FILE__) + '/service.sh.erb'
+    
+      def initialize(options)
+        super
+      
+        raise PlatformNotSupported, 'Running as a service only supported on Linux' unless Thin.linux?
+      end
+    
+      def config_path
+        @options[:all] || DEFAULT_CONFIG_PATH
+      end
+    
+      def start
+        run :start
+      end
+    
+      def stop
+        run :stop
+      end
+    
+      def restart
+        run :restart
+      end
+    
+      def install(config_files_path=DEFAULT_CONFIG_PATH)
+        if File.exist?(INITD_PATH)
+          log ">> Thin service already installed at #{INITD_PATH}"
+        else
+          log ">> Installing thin service at #{INITD_PATH} ..."
+          sh "mkdir -p #{File.dirname(INITD_PATH)}"
+          log "writing #{INITD_PATH}"        
+          File.open(INITD_PATH, 'w') do |f|
+            f << ERB.new(File.read(TEMPLATE)).result(binding)
+          end
+          sh "chmod +x #{INITD_PATH}" # Make executable
+        end
+      
+        sh "mkdir -p #{config_files_path}"
+
+        log ''
+        log "To configure thin to start at system boot:"
+        log "on RedHat like systems:"
+        log "  sudo /sbin/chkconfig --level 345 #{NAME} on"
+        log "on Debian-like systems (Ubuntu):"
+        log "  sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
+        log "on Gentoo:"
+        log "  sudo rc-update add #{NAME} default"
+        log ''
+        log "Then put your config files in #{config_files_path}"
+      end
+    
+      private
+        def run(command)
+          Dir[config_path + '/*'].each do |config|
+            log "[#{command}] #{config} ..."
+            Command.run(command, :config => config, :daemonize => true)
+          end
+        end
+      
+        def sh(cmd)
+          log cmd
+          system(cmd)
+        end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.sh.erb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/controllers/service.sh.erb
new file mode 100644 (file)
index 0000000..5b96548
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides:          thin
+# Required-Start:    $local_fs $remote_fs
+# Required-Stop:     $local_fs $remote_fs
+# Default-Start:     2 3 4 5
+# Default-Stop:      S 0 1 6
+# Short-Description: thin initscript
+# Description:       thin
+### END INIT INFO
+
+# Original author: Forrest Robertson
+
+# Do NOT "set -e"
+
+DAEMON=<%= Command.script %>
+SCRIPT_NAME=<%= INITD_PATH %>
+CONFIG_PATH=<%= config_files_path %>
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+case "$1" in
+  start)
+       $DAEMON start --all $CONFIG_PATH
+       ;;
+  stop)
+       $DAEMON stop --all $CONFIG_PATH
+       ;;
+  restart)
+       $DAEMON restart --all $CONFIG_PATH
+       ;;
+  *)
+       echo "Usage: $SCRIPT_NAME {start|stop|restart}" >&2
+       exit 3
+       ;;
+esac
+
+:
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/daemonizing.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/daemonizing.rb
new file mode 100644 (file)
index 0000000..576f19a
--- /dev/null
@@ -0,0 +1,180 @@
+require 'etc'
+require 'daemons' unless Thin.win?
+
+module Process
+  # Returns +true+ the process identied by +pid+ is running.
+  def running?(pid)
+    Process.getpgid(pid) != -1
+  rescue Errno::EPERM
+    true
+  rescue Errno::ESRCH
+    false
+  end
+  module_function :running?
+end
+
+module Thin
+  # Raised when the pid file already exist starting as a daemon.
+  class PidFileExist < RuntimeError; end
+  
+  # Module included in classes that can be turned into a daemon.
+  # Handle stuff like:
+  # * storing the PID in a file
+  # * redirecting output to the log file
+  # * changing processs privileges
+  # * killing the process gracefully
+  module Daemonizable
+    attr_accessor :pid_file, :log_file
+    
+    def self.included(base)
+      base.extend ClassMethods
+    end
+    
+    def pid
+      File.exist?(pid_file) ? open(pid_file).read.to_i : nil
+    end
+    
+    # Turns the current script into a daemon process that detaches from the console.
+    def daemonize
+      raise PlatformNotSupported, 'Daemonizing is not supported on Windows'     if Thin.win?
+      raise ArgumentError,        'You must specify a pid_file to daemonize' unless @pid_file
+      
+      remove_stale_pid_file
+      
+      pwd = Dir.pwd # Current directory is changed during daemonization, so store it
+      
+      # HACK we need to create the directory before daemonization to prevent a bug under 1.9
+      #      ignoring all signals when the directory is created after daemonization.
+      FileUtils.mkdir_p File.dirname(@pid_file)
+      FileUtils.mkdir_p File.dirname(@log_file)
+      
+      Daemonize.daemonize(File.expand_path(@log_file), name)
+      
+      Dir.chdir(pwd)
+      
+      write_pid_file
+      
+      at_exit do
+        log ">> Exiting!"
+        remove_pid_file
+      end
+    end
+
+    # Change privileges of the process
+    # to the specified user and group.
+    def change_privilege(user, group=user)
+      log ">> Changing process privilege to #{user}:#{group}"
+      
+      uid, gid = Process.euid, Process.egid
+      target_uid = Etc.getpwnam(user).uid
+      target_gid = Etc.getgrnam(group).gid
+
+      if uid != target_uid || gid != target_gid
+        # Change process ownership
+        Process.initgroups(user, target_gid)
+        Process::GID.change_privilege(target_gid)
+        Process::UID.change_privilege(target_uid)
+      end
+    rescue Errno::EPERM => e
+      log "Couldn't change user and group to #{user}:#{group}: #{e}"
+    end
+    
+    # Register a proc to be called to restart the server.
+    def on_restart(&block)
+      @on_restart = block
+    end
+    
+    # Restart the server.
+    def restart
+      if @on_restart
+        log '>> Restarting ...'
+        stop
+        remove_pid_file
+        @on_restart.call
+        exit!
+      end
+    end
+    
+    module ClassMethods
+      # Send a QUIT or INT (if timeout is +0+) signal the process which
+      # PID is stored in +pid_file+.
+      # If the process is still running after +timeout+, KILL signal is
+      # sent.
+      def kill(pid_file, timeout=60)
+        if timeout == 0
+          send_signal('INT', pid_file, timeout)
+        else
+          send_signal('QUIT', pid_file, timeout)
+        end
+      end
+      
+      # Restart the server by sending HUP signal.
+      def restart(pid_file)
+        send_signal('HUP', pid_file)
+      end
+      
+      # Send a +signal+ to the process which PID is stored in +pid_file+.
+      def send_signal(signal, pid_file, timeout=60)
+        if pid = read_pid_file(pid_file)
+          Logging.log "Sending #{signal} signal to process #{pid} ... "
+          Process.kill(signal, pid)
+          Timeout.timeout(timeout) do
+            sleep 0.1 while Process.running?(pid)
+          end
+        else
+          Logging.log "Can't stop process, no PID found in #{pid_file}"
+        end
+      rescue Timeout::Error
+        Logging.log "Timeout!"
+        force_kill pid_file
+      rescue Interrupt
+        force_kill pid_file
+      rescue Errno::ESRCH # No such process
+        Logging.log "process not found!"
+        force_kill pid_file
+      end
+      
+      def force_kill(pid_file)
+        if pid = read_pid_file(pid_file)
+          Logging.log "Sending KILL signal to process #{pid} ... "
+          Process.kill("KILL", pid)
+          File.delete(pid_file) if File.exist?(pid_file)
+        else
+          Logging.log "Can't stop process, no PID found in #{pid_file}"
+        end
+      end
+      
+      def read_pid_file(file)
+        if File.file?(file) && pid = File.read(file)
+          pid.to_i
+        else
+          nil
+        end
+      end
+    end
+    
+    protected
+      def remove_pid_file
+        File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
+      end
+    
+      def write_pid_file
+        log ">> Writing PID to #{@pid_file}"
+        open(@pid_file,"w") { |f| f.write(Process.pid) }
+        File.chmod(0644, @pid_file)
+      end
+      
+      # If PID file is stale, remove it.
+      def remove_stale_pid_file
+        if File.exist?(@pid_file)
+          if pid && Process.running?(pid)
+            raise PidFileExist, "#{@pid_file} already exists, seems like it's already running (process ID: #{pid}). " +
+                                "Stop the process or delete #{@pid_file}."
+          else
+            log ">> Deleting stale PID file #{@pid_file}"
+            remove_pid_file
+          end
+        end
+      end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/headers.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/headers.rb
new file mode 100644 (file)
index 0000000..3e1b9cc
--- /dev/null
@@ -0,0 +1,39 @@
+module Thin
+  # Store HTTP header name-value pairs direcly to a string
+  # and allow duplicated entries on some names.
+  class Headers
+    HEADER_FORMAT      = "%s: %s\r\n".freeze
+    ALLOWED_DUPLICATES = %w(Set-Cookie Set-Cookie2 Warning WWW-Authenticate).freeze
+    
+    def initialize
+      @sent = {}
+      @out = []
+    end
+    
+    # Add <tt>key: value</tt> pair to the headers.
+    # Ignore if already sent and no duplicates are allowed
+    # for this +key+.
+    def []=(key, value)
+      if !@sent.has_key?(key) || ALLOWED_DUPLICATES.include?(key)
+        @sent[key] = true
+        value = case value
+                when Time
+                  value.httpdate
+                when NilClass
+                  return
+                else
+                  value.to_s
+                end
+        @out << HEADER_FORMAT % [key, value]
+      end
+    end
+    
+    def has_key?(key)
+      @sent[key]
+    end
+    
+    def to_s
+      @out.join
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/logging.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/logging.rb
new file mode 100644 (file)
index 0000000..2a99827
--- /dev/null
@@ -0,0 +1,54 @@
+module Thin
+  # To be included in classes to allow some basic logging
+  # that can be silenced (<tt>Logging.silent=</tt>) or made
+  # more verbose.
+  # <tt>Logging.debug=</tt>: log all error backtrace and messages
+  #                          logged with +debug+.
+  # <tt>Logging.trace=</tt>: log all raw request and response and
+  #                          messages logged with +trace+.
+  module Logging
+    class << self
+      attr_writer :trace, :debug, :silent
+      
+      def trace?;  !@silent && @trace  end
+      def debug?;  !@silent && @debug  end
+      def silent?;  @silent            end
+    end
+    
+    # Global silencer methods
+    def silent
+      Logging.silent?
+    end
+    def silent=(value)
+      Logging.silent = value
+    end
+    
+    # Log a message to the console
+    def log(msg)
+      puts msg unless Logging.silent?
+    end
+    module_function :log
+    public :log
+    
+    # Log a message to the console if tracing is activated
+    def trace(msg=nil)
+      log msg || yield if Logging.trace?
+    end
+    module_function :trace
+    public :trace
+    
+    # Log a message to the console if debugging is activated
+    def debug(msg=nil)
+      log msg || yield if Logging.debug?
+    end
+    module_function :debug
+    public :debug
+    
+    # Log an error backtrace if debugging is activated
+    def log_error(e=$!)
+      debug "#{e}\n\t" + e.backtrace.join("\n\t")
+    end
+    module_function :log_error
+    public :log_error
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/request.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/request.rb
new file mode 100644 (file)
index 0000000..5c46efd
--- /dev/null
@@ -0,0 +1,156 @@
+require 'tempfile'
+
+module Thin
+  # Raised when an incoming request is not valid
+  # and the server can not process it.
+  class InvalidRequest < IOError; end
+
+  # A request sent by the client to the server.
+  class Request
+    # Maximum request body size before it is moved out of memory
+    # and into a tempfile for reading.
+    MAX_BODY          = 1024 * (80 + 32)
+    BODY_TMPFILE      = 'thin-body'.freeze
+    MAX_HEADER        = 1024 * (80 + 32)
+    
+    INITIAL_BODY      = ''
+    # Force external_encoding of request's body to ASCII_8BIT
+    INITIAL_BODY.encode!(Encoding::ASCII_8BIT) if INITIAL_BODY.respond_to?(:encode!)
+    
+    # Freeze some HTTP header names & values
+    SERVER_SOFTWARE   = 'SERVER_SOFTWARE'.freeze
+    SERVER_NAME       = 'SERVER_NAME'.freeze
+    LOCALHOST         = 'localhost'.freeze
+    HTTP_VERSION      = 'HTTP_VERSION'.freeze
+    HTTP_1_0          = 'HTTP/1.0'.freeze
+    REMOTE_ADDR       = 'REMOTE_ADDR'.freeze
+    CONTENT_LENGTH    = 'CONTENT_LENGTH'.freeze
+    CONNECTION        = 'HTTP_CONNECTION'.freeze
+    KEEP_ALIVE_REGEXP = /\bkeep-alive\b/i.freeze
+    CLOSE_REGEXP      = /\bclose\b/i.freeze
+    
+    # Freeze some Rack header names
+    RACK_INPUT        = 'rack.input'.freeze
+    RACK_VERSION      = 'rack.version'.freeze
+    RACK_ERRORS       = 'rack.errors'.freeze
+    RACK_MULTITHREAD  = 'rack.multithread'.freeze
+    RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
+    RACK_RUN_ONCE     = 'rack.run_once'.freeze
+    ASYNC_CALLBACK    = 'async.callback'.freeze
+    ASYNC_CLOSE       = 'async.close'.freeze
+
+    # CGI-like request environment variables
+    attr_reader :env
+
+    # Unparsed data of the request
+    attr_reader :data
+
+    # Request body
+    attr_reader :body
+
+    def initialize
+      @parser   = Thin::HttpParser.new
+      @data     = ''
+      @nparsed  = 0
+      @body     = StringIO.new(INITIAL_BODY.dup)
+      @env      = {
+        SERVER_SOFTWARE   => SERVER,
+        SERVER_NAME       => LOCALHOST,
+
+        # Rack stuff
+        RACK_INPUT        => @body,
+
+        RACK_VERSION      => VERSION::RACK,
+        RACK_ERRORS       => STDERR,
+
+        RACK_MULTITHREAD  => false,
+        RACK_MULTIPROCESS => false,
+        RACK_RUN_ONCE     => false
+      }
+    end
+
+    # Parse a chunk of data into the request environment
+    # Raises a +InvalidRequest+ if invalid.
+    # Returns +true+ if the parsing is complete.
+    def parse(data)
+      if @parser.finished?  # Header finished, can only be some more body
+        body << data
+      else                  # Parse more header using the super parser
+        @data << data
+        raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER
+
+        @nparsed = @parser.execute(@env, @data, @nparsed)
+
+        # Transfert to a tempfile if body is very big
+        move_body_to_tempfile if @parser.finished? && content_length > MAX_BODY
+      end
+
+
+      if finished?   # Check if header and body are complete
+        @data = nil
+        @body.rewind
+        true         # Request is fully parsed
+      else
+        false        # Not finished, need more data
+      end
+    end
+
+    # +true+ if headers and body are finished parsing
+    def finished?
+      @parser.finished? && @body.size >= content_length
+    end
+
+    # Expected size of the body
+    def content_length
+      @env[CONTENT_LENGTH].to_i
+    end
+
+    # Returns +true+ if the client expect the connection to be persistent.
+    def persistent?
+      # Clients and servers SHOULD NOT assume that a persistent connection
+      # is maintained for HTTP versions less than 1.1 unless it is explicitly
+      # signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)
+      if @env[HTTP_VERSION] == HTTP_1_0
+        @env[CONNECTION] =~ KEEP_ALIVE_REGEXP
+
+      # HTTP/1.1 client intends to maintain a persistent connection unless
+      # a Connection header including the connection-token "close" was sent
+      # in the request
+      else
+        @env[CONNECTION].nil? || @env[CONNECTION] !~ CLOSE_REGEXP
+      end
+    end
+
+    def remote_address=(address)
+      @env[REMOTE_ADDR] = address
+    end
+
+    def threaded=(value)
+      @env[RACK_MULTITHREAD] = value
+    end
+    
+    def async_callback=(callback)
+      @env[ASYNC_CALLBACK] = callback
+      @env[ASYNC_CLOSE] = EventMachine::DefaultDeferrable.new
+    end
+    
+    def async_close
+      @async_close ||= @env[ASYNC_CLOSE]
+    end
+
+    # Close any resource used by the request
+    def close
+      @body.delete if @body.class == Tempfile
+    end
+
+    private
+      def move_body_to_tempfile
+        current_body = @body
+        current_body.rewind
+        @body = Tempfile.new(BODY_TMPFILE)
+        @body.binmode
+        @body << current_body.read
+        @env[RACK_INPUT] = @body
+      end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/response.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/response.rb
new file mode 100644 (file)
index 0000000..21f51e4
--- /dev/null
@@ -0,0 +1,104 @@
+module Thin
+  # A response sent to the client.
+  class Response
+    CONNECTION     = 'Connection'.freeze
+    CLOSE          = 'close'.freeze
+    KEEP_ALIVE     = 'keep-alive'.freeze
+    SERVER         = 'Server'.freeze
+    CONTENT_LENGTH = 'Content-Length'.freeze
+
+    PERSISTENT_STATUSES  = [100, 101].freeze
+
+    # Status code
+    attr_accessor :status
+
+    # Response body, must respond to +each+.
+    attr_accessor :body
+
+    # Headers key-value hash
+    attr_reader   :headers
+
+    def initialize
+      @headers    = Headers.new
+      @status     = 200
+      @persistent = false
+    end
+
+    # String representation of the headers
+    # to be sent in the response.
+    def headers_output
+      # Set default headers
+      @headers[CONNECTION] = persistent? ? KEEP_ALIVE : CLOSE
+      @headers[SERVER]     = Thin::SERVER
+
+      @headers.to_s
+    end
+
+    # Top header of the response,
+    # containing the status code and response headers.
+    def head
+      "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
+    end
+
+    if Thin.ruby_18?
+
+      # Ruby 1.8 implementation.
+      # Respects Rack specs.
+      #
+      # See http://rack.rubyforge.org/doc/files/SPEC.html
+      def headers=(key_value_pairs)
+        key_value_pairs.each do |k, vs|
+          vs.each { |v| @headers[k] = v.chomp } if vs
+        end if key_value_pairs
+      end
+
+    else
+
+      # Ruby 1.9 doesn't have a String#each anymore.
+      # Rack spec doesn't take care of that yet, for now we just use
+      # +each+ but fallback to +each_line+ on strings.
+      # I wish we could remove that condition.
+      # To be reviewed when a new Rack spec comes out.
+      def headers=(key_value_pairs)
+        key_value_pairs.each do |k, vs|
+          next unless vs
+          if vs.is_a?(String)
+            vs.each_line { |v| @headers[k] = v.chomp }
+          else
+            vs.each { |v| @headers[k] = v.chomp }
+          end
+        end if key_value_pairs
+      end
+
+    end
+
+    # Close any resource used by the response
+    def close
+      @body.close if @body.respond_to?(:close)
+    end
+
+    # Yields each chunk of the response.
+    # To control the size of each chunk
+    # define your own +each+ method on +body+.
+    def each
+      yield head
+      if @body.is_a?(String)
+        yield @body
+      else
+        @body.each { |chunk| yield chunk }
+      end
+    end
+
+    # Tell the client the connection should stay open
+    def persistent!
+      @persistent = true
+    end
+
+    # Persistent connection must be requested as keep-alive
+    # from the server and have a Content-Length, or the response
+    # status must require that the connection remain open.
+    def persistent?
+      (@persistent && @headers.has_key?(CONTENT_LENGTH)) || PERSISTENT_STATUSES.include?(@status)
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/runner.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/runner.rb
new file mode 100644 (file)
index 0000000..60c5891
--- /dev/null
@@ -0,0 +1,220 @@
+require 'optparse'
+require 'yaml'
+
+module Thin  
+  # CLI runner.
+  # Parse options and send command to the correct Controller.
+  class Runner
+    COMMANDS            = %w(start stop restart config)
+    LINUX_ONLY_COMMANDS = %w(install)
+    
+    # Commands that wont load options from the config file
+    CONFIGLESS_COMMANDS = %w(config install)
+    
+    # Parsed options
+    attr_accessor :options
+    
+    # Name of the command to be runned.
+    attr_accessor :command
+    
+    # Arguments to be passed to the command.
+    attr_accessor :arguments
+    
+    # Return all available commands
+    def self.commands
+      commands  = COMMANDS
+      commands += LINUX_ONLY_COMMANDS if Thin.linux?
+      commands
+    end
+    
+    def initialize(argv)
+      @argv = argv
+      
+      # Default options values
+      @options = {
+        :chdir                => Dir.pwd,
+        :environment          => 'development',
+        :address              => '0.0.0.0',
+        :port                 => Server::DEFAULT_PORT,
+        :timeout              => Server::DEFAULT_TIMEOUT,
+        :log                  => 'log/thin.log',
+        :pid                  => 'tmp/pids/thin.pid',
+        :max_conns            => Server::DEFAULT_MAXIMUM_CONNECTIONS,
+        :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
+        :require              => [],
+        :wait                 => Controllers::Cluster::DEFAULT_WAIT_TIME
+      }
+      
+      parse!
+    end
+    
+    def parser
+      # NOTE: If you add an option here make sure the key in the +options+ hash is the
+      # same as the name of the command line option.
+      # +option+ keys are used to build the command line to launch other processes,
+      # see <tt>lib/thin/command.rb</tt>.
+      @parser ||= OptionParser.new do |opts|
+        opts.banner = "Usage: thin [options] #{self.class.commands.join('|')}"
+
+        opts.separator ""
+        opts.separator "Server options:"
+
+        opts.on("-a", "--address HOST", "bind to HOST address " +
+                                        "(default: #{@options[:address]})")             { |host| @options[:address] = host }
+        opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})")          { |port| @options[:port] = port.to_i }
+        opts.on("-S", "--socket FILE", "bind to unix domain socket")                    { |file| @options[:socket] = file }
+        opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply")                       { |key| @options[:swiftiply] = key }
+        opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)",
+                                        "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name }
+        opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " +
+                                       "Rack adapter")                                  { |file| @options[:rackup] = file }
+        opts.on("-c", "--chdir DIR", "Change to dir before starting")                   { |dir| @options[:chdir] = File.expand_path(dir) }
+        opts.on(      "--stats PATH", "Mount the Stats adapter under PATH")             { |path| @options[:stats] = path }
+
+        opts.separator ""
+        opts.separator "SSL options:"
+
+        opts.on(      "--ssl", "Enables SSL")                                           { @options[:ssl] = true }
+        opts.on(      "--ssl-key-file PATH", "Path to private key")                     { |path| @options[:ssl_key_file] = path }
+        opts.on(      "--ssl-cert-file PATH", "Path to certificate")                    { |path| @options[:ssl_cert_file] = path }
+        opts.on(      "--ssl-verify", "Enables SSL certificate verification")           { @options[:ssl_verify] = true }
+
+        opts.separator ""
+        opts.separator "Adapter options:"
+        opts.on("-e", "--environment ENV", "Framework environment " +                       
+                                           "(default: #{@options[:environment]})")      { |env| @options[:environment] = env }
+        opts.on(      "--prefix PATH", "Mount the app under PATH (start with /)")       { |path| @options[:prefix] = path }
+        
+        unless Thin.win? # Daemonizing not supported on Windows
+          opts.separator ""
+          opts.separator "Daemon options:"
+                                                                                      
+          opts.on("-d", "--daemonize", "Run daemonized in the background")              { @options[:daemonize] = true }
+          opts.on("-l", "--log FILE", "File to redirect output " +                      
+                                      "(default: #{@options[:log]})")                   { |file| @options[:log] = file }
+          opts.on("-P", "--pid FILE", "File to store PID " +                            
+                                      "(default: #{@options[:pid]})")                   { |file| @options[:pid] = file }
+          opts.on("-u", "--user NAME", "User to run daemon as (use with -g)")           { |user| @options[:user] = user }
+          opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)")         { |group| @options[:group] = group }
+          opts.on(      "--tag NAME", "Additional text to display in process listing")  { |tag| @options[:tag] = tag }
+                                                                                      
+          opts.separator ""
+          opts.separator "Cluster options:"                                             
+                                                                                      
+          opts.on("-s", "--servers NUM", "Number of servers to start")                  { |num| @options[:servers] = num.to_i }
+          opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i }
+          opts.on("-C", "--config FILE", "Load options from config file")               { |file| @options[:config] = file }
+          opts.on(      "--all [DIR]", "Send command to each config files in DIR")      { |dir| @options[:all] = dir } if Thin.linux?
+          opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true }
+          opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i }
+        end
+        
+        opts.separator ""
+        opts.separator "Tuning options:"
+        
+        opts.on("-b", "--backend CLASS", "Backend to use, full classname")              { |name| @options[:backend] = name }
+        opts.on("-t", "--timeout SEC", "Request or command timeout in sec " +            
+                                       "(default: #{@options[:timeout]})")              { |sec| @options[:timeout] = sec.to_i }
+        opts.on("-f", "--force", "Force the execution of the command")                  { @options[:force] = true }
+        opts.on(      "--max-conns NUM", "Maximum number of open file descriptors " +
+                                         "(default: #{@options[:max_conns]})",
+                                         "Might require sudo to set higher then 1024")  { |num| @options[:max_conns] = num.to_i } unless Thin.win?
+        opts.on(      "--max-persistent-conns NUM",
+                                       "Maximum number of persistent connections",
+                                       "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i }
+        opts.on(      "--threaded", "Call the Rack application in threads " +
+                                    "[experimental]")                                   { @options[:threaded] = true }
+        opts.on(      "--no-epoll", "Disable the use of epoll")                         { @options[:no_epoll] = true } if Thin.linux?
+        
+        opts.separator ""
+        opts.separator "Common options:"
+
+        opts.on_tail("-r", "--require FILE", "require the library")                     { |file| @options[:require] << file }
+        opts.on_tail("-D", "--debug", "Set debugging on")                               { @options[:debug] = true }
+        opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)")      { @options[:trace] = true }
+        opts.on_tail("-h", "--help", "Show this message")                               { puts opts; exit }
+        opts.on_tail('-v', '--version', "Show version")                                 { puts Thin::SERVER; exit }
+      end
+    end
+    
+    # Parse the options.
+    def parse!
+      parser.parse! @argv
+      @command   = @argv.shift
+      @arguments = @argv
+    end
+        
+    # Parse the current shell arguments and run the command.
+    # Exits on error.
+    def run!
+      if self.class.commands.include?(@command)
+        run_command
+      elsif @command.nil?
+        puts "Command required"
+        puts @parser
+        exit 1  
+      else
+        abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
+      end
+    end
+    
+    # Send the command to the controller: single instance or cluster.
+    def run_command
+      load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command)
+      
+      # PROGRAM_NAME is relative to the current directory, so make sure
+      # we store and expand it before changing directory.
+      Command.script = File.expand_path($PROGRAM_NAME)
+      
+      # Change the current directory ASAP so that all relative paths are
+      # relative to this one.
+      Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command)
+      
+      @options[:require].each { |r| ruby_require r }
+      Logging.debug = @options[:debug]
+      Logging.trace = @options[:trace]
+      
+      controller = case
+      when cluster? then Controllers::Cluster.new(@options)
+      when service? then Controllers::Service.new(@options)
+      else               Controllers::Controller.new(@options)
+      end
+      
+      if controller.respond_to?(@command)
+        begin
+          controller.send(@command, *@arguments)
+        rescue RunnerError => e
+          abort e.message
+        end
+      else
+        abort "Invalid options for command: #{@command}"
+      end
+    end
+    
+    # +true+ if we're controlling a cluster.
+    def cluster?
+      @options[:only] || @options[:servers] || @options[:config]
+    end
+    
+    # +true+ if we're acting a as system service.
+    def service?
+      @options.has_key?(:all) || @command == 'install'
+    end
+    
+    private
+      def load_options_from_config_file!
+        if file = @options.delete(:config)
+          YAML.load_file(file).each { |key, value| @options[key.to_sym] = value }
+        end
+      end
+      
+      def ruby_require(file)
+        if File.extname(file) == '.ru'
+          warn 'WARNING: Use the -R option to load a Rack config file'
+          @options[:rackup] = file
+        else
+          require file
+        end
+      end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/server.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/server.rb
new file mode 100644 (file)
index 0000000..7b91079
--- /dev/null
@@ -0,0 +1,253 @@
+module Thin
+  # The uterly famous Thin HTTP server.
+  # It listen for incoming request through a given +backend+
+  # and forward all request to +app+.
+  #
+  # == TCP server
+  # Create a new TCP server on bound to <tt>host:port</tt> by specifiying +host+
+  # and +port+ as the first 2 arguments.
+  #
+  #   Thin::Server.start('0.0.0.0', 3000, app)
+  #
+  # == UNIX domain server
+  # Create a new UNIX domain socket bound to +socket+ file by specifiying a filename
+  # as the first argument. Eg.: /tmp/thin.sock. If the first argument contains a <tt>/</tt>
+  # it will be assumed to be a UNIX socket. 
+  #
+  #   Thin::Server.start('/tmp/thin.sock', app)
+  #
+  # == Using a custom backend
+  # You can implement your own way to connect the server to its client by creating your
+  # own Backend class and pass it as the :backend option.
+  #
+  #   Thin::Server.start('galaxy://faraway', 1345, app, :backend => Thin::Backends::MyFancyBackend)
+  #
+  # == Rack application (+app+)
+  # All requests will be processed through +app+ that must be a valid Rack adapter.
+  # A valid Rack adapter (application) must respond to <tt>call(env#Hash)</tt> and
+  # return an array of <tt>[status, headers, body]</tt>.
+  #
+  # == Building an app in place
+  # If a block is passed, a <tt>Rack::Builder</tt> instance
+  # will be passed to build the +app+. So you can do cool stuff like this:
+  # 
+  #   Thin::Server.start('0.0.0.0', 3000) do
+  #     use Rack::CommonLogger
+  #     use Rack::ShowExceptions
+  #     map "/lobster" do
+  #       use Rack::Lint
+  #       run Rack::Lobster.new
+  #     end
+  #   end
+  #
+  # == Controlling with signals
+  # * QUIT: Gracefull shutdown (see Server#stop)
+  # * INT and TERM: Force shutdown (see Server#stop!)
+  # Disable signals by passing <tt>:signals => false</tt>
+  # 
+  class Server
+    include Logging
+    include Daemonizable
+    extend  Forwardable
+    
+    # Default values
+    DEFAULT_TIMEOUT                        = 30 #sec
+    DEFAULT_HOST                           = '0.0.0.0'
+    DEFAULT_PORT                           = 3000
+    DEFAULT_MAXIMUM_CONNECTIONS            = 1024
+    DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS = 512
+        
+    # Application (Rack adapter) called with the request that produces the response.
+    attr_accessor :app
+
+    # A tag that will show in the process listing
+    attr_accessor :tag
+
+    # Backend handling the connections to the clients.
+    attr_accessor :backend
+    
+    # Maximum number of seconds for incoming data to arrive before the connection
+    # is dropped.
+    def_delegators :backend, :timeout, :timeout=
+    
+    # Maximum number of file or socket descriptors that the server may open.
+    def_delegators :backend, :maximum_connections, :maximum_connections=
+    
+    # Maximum number of connection that can be persistent at the same time.
+    # Most browser never close the connection so most of the time they are closed
+    # when the timeout occur. If we don't control the number of persistent connection,
+    # if would be very easy to overflow the server for a DoS attack.
+    def_delegators :backend, :maximum_persistent_connections, :maximum_persistent_connections=
+    
+    # Allow using threads in the backend.
+    def_delegators :backend, :threaded?, :threaded=
+    
+    # Allow using SSL in the backend.
+    def_delegators :backend, :ssl?, :ssl=, :ssl_options=
+    
+    # Address and port on which the server is listening for connections.
+    def_delegators :backend, :host, :port
+    
+    # UNIX domain socket on which the server is listening for connections.
+    def_delegator :backend, :socket
+    
+    # Disable the use of epoll under Linux
+    def_delegators :backend, :no_epoll, :no_epoll=
+    
+    def initialize(*args, &block)
+      host, port, options = DEFAULT_HOST, DEFAULT_PORT, {}
+      
+      # Guess each parameter by its type so they can be
+      # received in any order.
+      args.each do |arg|
+        case arg
+        when Fixnum, /^\d+$/ then port    = arg.to_i
+        when String          then host    = arg
+        when Hash            then options = arg
+        else
+          @app = arg if arg.respond_to?(:call)
+        end
+      end
+      
+      # Set tag if needed
+      self.tag = options[:tag]
+
+      # Try to intelligently select which backend to use.
+      @backend = select_backend(host, port, options)
+      
+      load_cgi_multipart_eof_fix
+      
+      @backend.server = self
+      
+      # Set defaults
+      @backend.maximum_connections            = DEFAULT_MAXIMUM_CONNECTIONS
+      @backend.maximum_persistent_connections = DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
+      @backend.timeout                        = DEFAULT_TIMEOUT
+      
+      # Allow using Rack builder as a block
+      @app = Rack::Builder.new(&block).to_app if block
+      
+      # If in debug mode, wrap in logger adapter
+      @app = Rack::CommonLogger.new(@app) if Logging.debug?
+      
+      setup_signals unless options[:signals].class == FalseClass
+    end
+    
+    # Lil' shortcut to turn this:
+    # 
+    #   Server.new(...).start
+    # 
+    # into this:
+    # 
+    #   Server.start(...)
+    # 
+    def self.start(*args, &block)
+      new(*args, &block).start!
+    end
+        
+    # Start the server and listen for connections.
+    def start
+      raise ArgumentError, 'app required' unless @app
+      
+      log   ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
+      debug ">> Debugging ON"
+      trace ">> Tracing ON"
+      
+      log ">> Maximum connections set to #{@backend.maximum_connections}"
+      log ">> Listening on #{@backend}, CTRL+C to stop"
+      
+      @backend.start
+    end
+    alias :start! :start
+    
+    # == Gracefull shutdown
+    # Stops the server after processing all current connections.
+    # As soon as this method is called, the server stops accepting
+    # new requests and wait for all current connections to finish.
+    # Calling twice is the equivalent of calling <tt>stop!</tt>.
+    def stop
+      if running?
+        @backend.stop
+        unless @backend.empty?
+          log ">> Waiting for #{@backend.size} connection(s) to finish, " +
+                 "can take up to #{timeout} sec, CTRL+C to stop now"
+        end
+      else
+        stop!
+      end
+    end
+    
+    # == Force shutdown
+    # Stops the server closing all current connections right away.
+    # This doesn't wait for connection to finish their work and send data.
+    # All current requests will be dropped.
+    def stop!
+      log ">> Stopping ..."
+
+      @backend.stop!
+    end
+    
+    # == Configure the server
+    # The process might need to have superuser privilege to configure
+    # server with optimal options.
+    def config
+      @backend.config
+    end
+    
+    # Name of the server and type of backend used.
+    # This is also the name of the process in which Thin is running as a daemon.
+    def name
+      "thin server (#{@backend})" + (tag ? " [#{tag}]" : "")
+    end
+    alias :to_s :name
+    
+    # Return +true+ if the server is running and ready to receive requests.
+    # Note that the server might still be running and return +false+ when
+    # shuting down and waiting for active connections to complete.
+    def running?
+      @backend.running?
+    end
+    
+    protected
+      # Register signals:
+      # * INT calls +stop+ to shutdown gracefully.
+      # * TERM calls <tt>stop!</tt> to force shutdown.
+      def setup_signals
+        trap('INT')  { stop! }
+        trap('TERM') { stop! }
+        unless Thin.win?
+          trap('QUIT') { stop }
+          trap('HUP')  { restart }
+        end
+      end
+      
+      def select_backend(host, port, options)
+        case
+        when options.has_key?(:backend)
+          raise ArgumentError, ":backend must be a class" unless options[:backend].is_a?(Class)
+          options[:backend].new(host, port, options)
+        when options.has_key?(:swiftiply)
+          Backends::SwiftiplyClient.new(host, port, options)
+        when host.include?('/')
+          Backends::UnixServer.new(host)
+        else
+          Backends::TcpServer.new(host, port)
+        end
+      end
+      
+      # Taken from Mongrel cgi_multipart_eof_fix
+      # Ruby 1.8.5 has a security bug in cgi.rb, we need to patch it.
+      def load_cgi_multipart_eof_fix
+        version = RUBY_VERSION.split('.').map { |i| i.to_i }
+        
+        if version[0] <= 1 && version[1] <= 8 && version[2] <= 5 && RUBY_PLATFORM !~ /java/
+          begin
+            require 'cgi_multipart_eof_fix'
+          rescue LoadError
+            log "!! Ruby 1.8.5 is not secure please install cgi_multipart_eof_fix:"
+            log "   gem install cgi_multipart_eof_fix"
+          end
+        end
+      end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.html.erb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.html.erb
new file mode 100644 (file)
index 0000000..14338bf
--- /dev/null
@@ -0,0 +1,216 @@
+<%#
+# Taken from Rack::ShowException
+# adapted from Django <djangoproject.com>
+# Copyright (c) 2005, the Lawrence Journal-World
+# Used under the modified BSD license:
+# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
+%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+  <meta name="robots" content="NONE,NOARCHIVE" />
+  <title>Thin Stats</title>
+  <style type="text/css">
+    html * { padding:0; margin:0; }
+    body * { padding:10px 20px; }
+    body * * { padding:0; }
+    body { font:small sans-serif; }
+    body>div { border-bottom:1px solid #ddd; }
+    h1 { font-weight:normal; }
+    h2 { margin-bottom:.8em; }
+    h2 span { font-size:80%; color:#666; font-weight:normal; }
+    h3 { margin:1em 0 .5em 0; }
+    h4 { margin:0 0 .5em 0; font-weight: normal; }
+    table {
+        border:1px solid #ccc; border-collapse: collapse; background:white; }
+    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
+    thead th {
+        padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
+        font-weight:normal; font-size:11px; border:1px solid #ddd; }
+    tbody th { text-align:right; color:#666; padding-right:.5em; }
+    table.vars { margin:5px 0 2px 40px; }
+    table.vars td, table.req td { font-family:monospace; }
+    table td.code { width:100%;}
+    table td.code div { overflow:hidden; }
+    table.source th { color:#666; }
+    table.source td {
+        font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
+    ul.traceback { list-style-type:none; }
+    ul.traceback li.frame { margin-bottom:1em; }
+    div.context { margin: 10px 0; }
+    div.context ol {
+        padding-left:30px; margin:0 10px; list-style-position: inside; }
+    div.context ol li {
+        font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
+    div.context ol.context-line li { color:black; background-color:#ccc; }
+    div.context ol.context-line li span { float: right; }
+    div.commands { margin-left: 40px; }
+    div.commands a { color:black; text-decoration:none; }
+    #summary { background: #ffc; }
+    #summary h2 { font-weight: normal; color: #666; }
+    #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
+    #summary ul#quicklinks li { float: left; padding: 0 1em; }
+    #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
+    #explanation { background:#eee; }
+    #template, #template-not-exist { background:#f6f6f6; }
+    #template-not-exist ul { margin: 0 0 0 20px; }
+    #traceback { background:#eee; }
+    #summary table { border:none; background:transparent; }
+    #requests { background:#f6f6f6; padding-left:120px; }
+    #requests h2, #requests h3 { position:relative; margin-left:-100px; }
+    #requests h3 { margin-bottom:-1em; }
+    .error { background: #ffc; }
+    .specific { color:#cc3300; font-weight:bold; }
+  </style>
+</head>
+<body>
+
+<div id="summary">
+  <h1>Server stats</h1>
+  <h2><%= Thin::SERVER %></h2>
+  <table>
+    <tr>
+      <th>Uptime</th>
+      <td><%= Time.now - @start_time %> sec</td>
+    </tr>
+    <tr>
+      <th>PID</th>
+      <td><%=h Process.pid %></td>
+    </tr>
+  </table>
+  
+  <% if @last_request %>
+    <h3>Jump to:</h3>
+    <ul id="quicklinks">
+      <li><a href="#get-info">GET</a></li>
+      <li><a href="#post-info">POST</a></li>
+      <li><a href="#cookie-info">Cookies</a></li>
+      <li><a href="#env-info">ENV</a></li>
+    </ul>
+  <% end %>
+</div>
+
+<div id="stats">
+  <h2>Requests</h2>
+  <h3>Stats</h3>
+  <table class="req">
+    <tr>
+      <td>Requests</td>
+      <td><%= @requests %></td>
+    </tr>
+    <tr>
+      <td>Finished</td>
+      <td><%= @requests_finished %></td>
+    </tr>
+    <tr>
+      <td>Errors</td>
+      <td><%= @requests - @requests_finished %></td>
+    </tr>
+    <tr>
+      <td>Last request</td>
+      <td><%= @last_request_time %> sec</td>
+    </tr>
+  </table>
+</div>
+
+<% if @last_request %>
+  <div id="requestinfo">
+    <h2>Last Request information</h2>
+
+    <h3 id="get-info">GET</h3>
+    <% unless @last_request.GET.empty? %>
+      <table class="req">
+        <thead>
+          <tr>
+            <th>Variable</th>
+            <th>Value</th>
+          </tr>
+        </thead>
+        <tbody>
+            <% @last_request.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
+            <tr>
+              <td><%=h key %></td>
+              <td class="code"><div><%=h val.inspect %></div></td>
+            </tr>
+            <% } %>
+        </tbody>
+      </table>
+    <% else %>
+      <p>No GET data.</p>
+    <% end %>
+
+    <h3 id="post-info">POST</h3>
+    <% unless @last_request.POST.empty? %>
+      <table class="req">
+        <thead>
+          <tr>
+            <th>Variable</th>
+            <th>Value</th>
+          </tr>
+        </thead>
+        <tbody>
+            <% @last_request.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
+            <tr>
+              <td><%=h key %></td>
+              <td class="code"><div><%=h val.inspect %></div></td>
+            </tr>
+            <% } %>
+        </tbody>
+      </table>
+    <% else %>
+      <p>No POST data.</p>
+    <% end %>
+
+
+    <h3 id="cookie-info">COOKIES</h3>
+    <% unless @last_request.cookies.empty? %>
+      <table class="req">
+        <thead>
+          <tr>
+            <th>Variable</th>
+            <th>Value</th>
+          </tr>
+        </thead>
+        <tbody>
+          <% @last_request.cookies.each { |key, val| %>
+            <tr>
+              <td><%=h key %></td>
+              <td class="code"><div><%=h val.inspect %></div></td>
+            </tr>
+          <% } %>
+        </tbody>
+      </table>
+    <% else %>
+      <p>No cookie data.</p>
+    <% end %>
+
+    <h3 id="env-info">Rack ENV</h3>
+      <table class="req">
+        <thead>
+          <tr>
+            <th>Variable</th>
+            <th>Value</th>
+          </tr>
+        </thead>
+        <tbody>
+            <% @last_request.env.sort_by { |k, v| k.to_s }.each { |key, val| %>
+            <tr>
+              <td><%=h key %></td>
+              <td class="code"><div><%=h val %></div></td>
+            </tr>
+            <% } %>
+        </tbody>
+      </table>
+
+  </div>
+<% end %>
+
+<div id="explanation">
+  <p>
+    You're seeing this page because you use <code>Thin::Stats</code>.
+  </p>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/stats.rb
new file mode 100644 (file)
index 0000000..146baec
--- /dev/null
@@ -0,0 +1,52 @@
+require 'erb'
+
+module Thin
+  module Stats
+    # Rack adapter to log stats about a Rack application.
+    class Adapter
+      include ERB::Util
+      
+      def initialize(app, path='/stats')
+        @app  = app
+        @path = path
+
+        @template = ERB.new(File.read(File.dirname(__FILE__) + '/stats.html.erb'))
+        
+        @requests          = 0
+        @requests_finished = 0
+        @start_time        = Time.now
+      end
+      
+      def call(env)
+        if env['PATH_INFO'].index(@path) == 0
+          serve(env)
+        else
+          log(env) { @app.call(env) }
+        end
+      end
+      
+      def log(env)
+        @requests += 1
+        @last_request = Rack::Request.new(env)
+        request_started_at = Time.now
+        
+        response = yield
+        
+        @requests_finished += 1
+        @last_request_time = Time.now - request_started_at
+        
+        response
+      end
+      
+      def serve(env)
+        body = @template.result(binding)
+        
+        [
+          200,
+          { 'Content-Type' => 'text/html' },
+          [body]
+        ]
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/statuses.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/statuses.rb
new file mode 100644 (file)
index 0000000..8aa3614
--- /dev/null
@@ -0,0 +1,43 @@
+module Thin
+  # Every standard HTTP code mapped to the appropriate message.
+  # Stolent from Mongrel.
+  HTTP_STATUS_CODES = {  
+    100  => 'Continue', 
+    101  => 'Switching Protocols', 
+    200  => 'OK', 
+    201  => 'Created', 
+    202  => 'Accepted', 
+    203  => 'Non-Authoritative Information', 
+    204  => 'No Content', 
+    205  => 'Reset Content', 
+    206  => 'Partial Content', 
+    300  => 'Multiple Choices', 
+    301  => 'Moved Permanently', 
+    302  => 'Moved Temporarily', 
+    303  => 'See Other', 
+    304  => 'Not Modified', 
+    305  => 'Use Proxy', 
+    400  => 'Bad Request', 
+    401  => 'Unauthorized', 
+    402  => 'Payment Required', 
+    403  => 'Forbidden', 
+    404  => 'Not Found', 
+    405  => 'Method Not Allowed', 
+    406  => 'Not Acceptable', 
+    407  => 'Proxy Authentication Required', 
+    408  => 'Request Time-out', 
+    409  => 'Conflict', 
+    410  => 'Gone', 
+    411  => 'Length Required', 
+    412  => 'Precondition Failed', 
+    413  => 'Request Entity Too Large', 
+    414  => 'Request-URI Too Large', 
+    415  => 'Unsupported Media Type', 
+    500  => 'Internal Server Error', 
+    501  => 'Not Implemented', 
+    502  => 'Bad Gateway', 
+    503  => 'Service Unavailable', 
+    504  => 'Gateway Time-out', 
+    505  => 'HTTP Version not supported'
+  }
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/version.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/lib/thin/version.rb
new file mode 100644 (file)
index 0000000..cc41201
--- /dev/null
@@ -0,0 +1,32 @@
+module Thin  
+  # Raised when a feature is not supported on the
+  # current platform.
+  class PlatformNotSupported < RuntimeError; end
+  
+  module VERSION #:nodoc:
+    MAJOR    = 1
+    MINOR    = 2
+    TINY     = 11
+    
+    STRING   = [MAJOR, MINOR, TINY].join('.')
+    
+    CODENAME = "Bat-Shit Crazy".freeze
+    
+    RACK     = [1, 0].freeze # Rack protocol version
+  end
+  
+  NAME    = 'thin'.freeze
+  SERVER  = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze  
+  
+  def self.win?
+    RUBY_PLATFORM =~ /mswin|mingw/
+  end
+  
+  def self.linux?
+    RUBY_PLATFORM =~ /linux/
+  end
+  
+  def self.ruby_18?
+    RUBY_VERSION =~ /^1\.8/
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/swiftiply_client_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/swiftiply_client_spec.rb
new file mode 100644 (file)
index 0000000..baebd6f
--- /dev/null
@@ -0,0 +1,66 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Backends::SwiftiplyClient do
+  before do
+    @backend = Backends::SwiftiplyClient.new('0.0.0.0', 3333)
+    @backend.server = mock('server', :null_object => true)
+  end
+  
+  it "should connect" do
+    EventMachine.run do
+      @backend.connect
+      EventMachine.stop
+    end
+  end
+  
+  it "should disconnect" do
+    EventMachine.run do
+      @backend.connect
+      @backend.disconnect
+      EventMachine.stop
+    end
+  end
+end
+
+describe SwiftiplyConnection do
+  before do
+    @connection = SwiftiplyConnection.new(nil)
+    @connection.backend = Backends::SwiftiplyClient.new('0.0.0.0', 3333)
+    @connection.backend.server = mock('server', :null_object => true)
+  end
+  
+  it do
+    @connection.should be_persistent
+  end
+  
+  it "should send handshake on connection_completed" do
+    @connection.should_receive(:send_data).with('swiftclient000000000d0500')
+    @connection.connection_completed
+  end
+  
+  it "should reconnect on unbind" do
+    @connection.backend.stub!(:running?).and_return(true)
+    @connection.stub!(:rand).and_return(0) # Make sure we don't wait
+    
+    @connection.should_receive(:reconnect).with('0.0.0.0', 3333)
+    
+    EventMachine.run do
+      @connection.unbind
+      EventMachine.add_timer(0) { EventMachine.stop }      
+    end
+  end
+  
+  it "should not reconnect when not running" do
+    @connection.backend.stub!(:running?).and_return(false)
+    EventMachine.should_not_receive(:add_timer)
+    @connection.unbind
+  end
+  
+  it "should have a host_ip" do
+    @connection.send(:host_ip).should == [0, 0, 0, 0]
+  end
+  
+  it "should generate swiftiply_handshake based on key" do
+    @connection.send(:swiftiply_handshake, 'key').should == 'swiftclient000000000d0503key'
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/tcp_server_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/tcp_server_spec.rb
new file mode 100644 (file)
index 0000000..f358e32
--- /dev/null
@@ -0,0 +1,33 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Backends::TcpServer do
+  before do
+    @backend = Backends::TcpServer.new('0.0.0.0', 3333)
+  end
+  
+  it "should not use epoll" do
+    @backend.no_epoll = true
+    EventMachine.should_not_receive(:epoll)
+    @backend.config
+  end
+  
+  it "should use epoll" do
+    EventMachine.should_receive(:epoll)
+    @backend.config
+  end
+  
+  it "should connect" do
+    EventMachine.run do
+      @backend.connect
+      EventMachine.stop
+    end
+  end
+  
+  it "should disconnect" do
+    EventMachine.run do
+      @backend.connect
+      @backend.disconnect
+      EventMachine.stop
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/unix_server_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/backends/unix_server_spec.rb
new file mode 100644 (file)
index 0000000..1abcdb6
--- /dev/null
@@ -0,0 +1,37 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Backends::UnixServer do
+  before do
+    @backend = Backends::UnixServer.new('/tmp/thin-test.sock')
+  end
+  
+  it "should connect" do
+    EventMachine.run do
+      @backend.connect
+      EventMachine.stop
+    end
+  end
+  
+  it "should disconnect" do
+    EventMachine.run do
+      @backend.connect
+      @backend.disconnect
+      EventMachine.stop
+    end
+  end
+  
+  it "should remove socket file on close" do
+    @backend.close
+    File.exist?('/tmp/thin-test.sock').should be_false
+  end
+end
+
+describe UnixConnection do
+  before do
+    @connection = UnixConnection.new(nil)
+  end
+  
+  it "should return 127.0.0.1 as remote_address" do
+    @connection.remote_address.should == '127.0.0.1'
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/command_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/command_spec.rb
new file mode 100644 (file)
index 0000000..751d11f
--- /dev/null
@@ -0,0 +1,25 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Command do
+  before do
+    Command.script = 'thin'
+    @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log',
+                           :require => %w(rubygems thin), :no_epoll => true)
+  end
+  
+  it 'should shellify command' do
+    out = @command.shellify
+    out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
+    out.should_not include('--pid')
+  end
+  
+  it 'should shellify Array argument to multiple parameters' do
+    out = @command.shellify
+    out.should include('--require="rubygems"', '--require="thin"')
+  end
+
+  it 'should convert _ to - in option name' do
+    out = @command.shellify
+    out.should include('--no-epoll')
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/cluster.yml b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/cluster.yml
new file mode 100644 (file)
index 0000000..5243635
--- /dev/null
@@ -0,0 +1,9 @@
+--- 
+pid: tmp/pids/thin.pid
+log: log/thin.log
+timeout: 60
+port: 5000
+chdir: spec/rails_app
+environment: production
+servers: 3
+address: 127.0.0.1
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/single.yml b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/configs/single.yml
new file mode 100644 (file)
index 0000000..29c3d61
--- /dev/null
@@ -0,0 +1,9 @@
+--- 
+pid: tmp/pids/thin.pid
+log: log/thin.log
+timeout: 60
+port: 6000
+chdir: spec/rails_app
+environment: production
+daemonize: true
+address: 127.0.0.1
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/connection_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/connection_spec.rb
new file mode 100644 (file)
index 0000000..6868f8d
--- /dev/null
@@ -0,0 +1,107 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Connection do
+  before do
+    @connection = Connection.new(mock('EM', :null_object => true))
+    @connection.post_init
+    @connection.backend = mock("backend", :ssl? => false)
+    @connection.app = proc do |env|
+      [200, {}, ['']]
+    end
+  end
+  
+  it "should parse on receive_data" do
+    @connection.request.should_receive(:parse).with('GET')
+    @connection.receive_data('GET')
+  end
+
+  it "should close connection on InvalidRequest error in receive_data" do
+    @connection.request.stub!(:parse).and_raise(InvalidRequest)
+    @connection.should_receive(:close_connection)
+    @connection.receive_data('')
+  end
+  
+  it "should process when parsing complete" do
+    @connection.request.should_receive(:parse).and_return(true)
+    @connection.should_receive(:process)
+    @connection.receive_data('GET')
+  end
+  
+  it "should process" do
+    @connection.process
+  end
+  
+  it "should rescue error in process" do
+    @connection.app.should_receive(:call).and_raise(StandardError)
+    @connection.process
+  end
+  
+  it "should rescue Timeout error in process" do
+    @connection.app.should_receive(:call).and_raise(Timeout::Error.new("timeout error not rescued"))
+    @connection.process
+  end
+  
+  it "should not return HTTP_X_FORWARDED_FOR as remote_address" do
+    @connection.request.env['HTTP_X_FORWARDED_FOR'] = '1.2.3.4'
+    @connection.stub!(:socket_address).and_return("127.0.0.1")
+    @connection.remote_address.should == "127.0.0.1"
+  end
+  
+  it "should return nil on error retreiving remote_address" do
+    @connection.stub!(:get_peername).and_raise(RuntimeError)
+    @connection.remote_address.should be_nil
+  end
+  
+  it "should return nil on nil get_peername" do
+    @connection.stub!(:get_peername).and_return(nil)
+    @connection.remote_address.should be_nil
+  end
+  
+  it "should return nil on empty get_peername" do
+    @connection.stub!(:get_peername).and_return('')
+    @connection.remote_address.should be_nil
+  end
+  
+  it "should return remote_address" do
+    @connection.stub!(:get_peername).and_return(Socket.pack_sockaddr_in(3000, '127.0.0.1'))
+    @connection.remote_address.should == '127.0.0.1'
+  end
+  
+  it "should not be persistent" do
+    @connection.should_not be_persistent
+  end
+
+  it "should be persistent when response is and allowed" do
+    @connection.response.stub!(:persistent?).and_return(true)
+    @connection.can_persist!
+    @connection.should be_persistent
+  end
+
+  it "should not be persistent when response is but not allowed" do
+    @connection.response.persistent!
+    @connection.should_not be_persistent
+  end
+  
+  it "should set request env as rack.multithread" do
+    EventMachine.should_receive(:defer)
+    
+    @connection.threaded = true
+    @connection.process
+    
+    @connection.request.env["rack.multithread"].should == true
+  end
+  
+  it "should set as threaded when app.deferred? is true" do
+    @connection.app.should_receive(:deferred?).and_return(true)
+    @connection.should be_threaded
+  end
+  
+  it "should not set as threaded when app.deferred? is false" do
+    @connection.app.should_receive(:deferred?).and_return(false)
+    @connection.should_not be_threaded
+  end
+
+  it "should not set as threaded when app do not respond to deferred?" do
+    @connection.should_not be_threaded
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/cluster_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/cluster_spec.rb
new file mode 100644 (file)
index 0000000..8124664
--- /dev/null
@@ -0,0 +1,267 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+include Controllers
+
+describe Cluster, "with host and port" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :address => '0.0.0.0',
+                           :port => 3000, 
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid'
+                          )
+  end
+    
+  it 'should include port number in file names' do
+    @cluster.send(:include_server_number, 'thin.log', 3000).should == 'thin.3000.log'
+    @cluster.send(:include_server_number, 'thin.pid', 3000).should == 'thin.3000.pid'
+  end
+  
+  it 'should call each server' do
+    calls = []
+    @cluster.send(:with_each_server) do |port|
+      calls << port
+    end
+    calls.should == [3000, 3001, 3002]
+  end
+    
+  it 'should start on each port' do
+    Command.should_receive(:run).with(:start, options_for_port(3000))
+    Command.should_receive(:run).with(:start, options_for_port(3001))
+    Command.should_receive(:run).with(:start, options_for_port(3002))
+
+    @cluster.start
+  end
+
+  it 'should stop on each port' do
+    Command.should_receive(:run).with(:stop, options_for_port(3000))
+    Command.should_receive(:run).with(:stop, options_for_port(3001))
+    Command.should_receive(:run).with(:stop, options_for_port(3002))
+
+    @cluster.stop
+  end
+  
+  private
+    def options_for_port(port)
+      { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
+    end
+end
+
+describe Cluster, "with UNIX socket" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :socket => '/tmp/thin.sock',
+                           :address => '0.0.0.0',
+                           :port => 3000,
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid'
+                          )
+  end
+  
+  it 'should include socket number in file names' do
+    @cluster.send(:include_server_number, 'thin.sock', 0).should == 'thin.0.sock'
+    @cluster.send(:include_server_number, 'thin', 0).should == 'thin.0'
+  end
+  
+  it "should exclude :address and :port options" do
+    @cluster.options.should_not have_key(:address)
+    @cluster.options.should_not have_key(:port)
+  end
+  
+  it 'should call each server' do
+    calls = []
+    @cluster.send(:with_each_server) do |n|
+      calls << n
+    end
+    calls.should == [0, 1, 2]
+  end
+  
+  it 'should start each server' do
+    Command.should_receive(:run).with(:start, options_for_socket(0))
+    Command.should_receive(:run).with(:start, options_for_socket(1))
+    Command.should_receive(:run).with(:start, options_for_socket(2))
+
+    @cluster.start
+  end
+
+  it 'should stop each server' do
+    Command.should_receive(:run).with(:stop, options_for_socket(0))
+    Command.should_receive(:run).with(:stop, options_for_socket(1))
+    Command.should_receive(:run).with(:stop, options_for_socket(2))
+
+    @cluster.stop
+  end
+  
+  
+  private
+    def options_for_socket(number)
+      { :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :socket => "/tmp/thin.#{number}.sock", :pid => "thin.#{number}.pid", :chdir => "/rails_app" }
+    end
+end
+
+describe Cluster, "controlling only one server" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :address => '0.0.0.0',
+                           :port => 3000, 
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid',
+                           :only => 3001
+                          )
+  end
+  
+  it 'should call only specified server' do
+    calls = []
+    @cluster.send(:with_each_server) do |n|
+      calls << n
+    end
+    calls.should == [3001]
+  end
+  
+  it "should start only specified server" do
+    Command.should_receive(:run).with(:start, options_for_port(3001))
+
+    @cluster.start
+  end
+  
+  private
+    def options_for_port(port)
+      { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
+    end
+end
+
+describe Cluster, "controlling only one server with UNIX socket" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :socket => '/tmp/thin.sock',
+                           :address => '0.0.0.0',
+                           :port => 3000,
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid',
+                           :only => 1
+                          )
+  end
+  
+  it 'should call only specified server' do
+    calls = []
+    @cluster.send(:with_each_server) do |n|
+      calls << n
+    end
+    calls.should == [1]
+  end
+end
+
+describe Cluster, "controlling only one server, by sequence number" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :address => '0.0.0.0',
+                           :port => 3000, 
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid',
+                           :only => 1
+                          )
+  end
+  
+  it 'should call only specified server' do
+    calls = []
+    @cluster.send(:with_each_server) do |n|
+      calls << n
+    end
+    calls.should == [3001]
+  end
+  
+  it "should start only specified server" do
+    Command.should_receive(:run).with(:start, options_for_port(3001))
+
+    @cluster.start
+  end
+  
+  private
+    def options_for_port(port)
+      { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
+    end
+end
+
+describe Cluster, "with Swiftiply" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :address => '0.0.0.0',
+                           :port => 3000, 
+                           :servers => 3,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid',
+                           :swiftiply => true
+                          )
+  end
+  
+  it 'should call each server' do
+    calls = []
+    @cluster.send(:with_each_server) do |n|
+      calls << n
+    end
+    calls.should == [0, 1, 2]
+  end
+  
+  it 'should start each server' do
+    Command.should_receive(:run).with(:start, options_for_swiftiply(0))
+    Command.should_receive(:run).with(:start, options_for_swiftiply(1))
+    Command.should_receive(:run).with(:start, options_for_swiftiply(2))
+
+    @cluster.start
+  end
+
+  it 'should stop each server' do
+    Command.should_receive(:run).with(:stop, options_for_swiftiply(0))
+    Command.should_receive(:run).with(:stop, options_for_swiftiply(1))
+    Command.should_receive(:run).with(:stop, options_for_swiftiply(2))
+
+    @cluster.stop
+  end
+  
+  private
+    def options_for_swiftiply(number)
+      { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
+    end
+end
+
+describe Cluster, "rolling restart" do
+  before do
+    @cluster = Cluster.new(:chdir => '/rails_app',
+                           :address => '0.0.0.0',
+                           :port => 3000, 
+                           :servers => 2,
+                           :timeout => 10,
+                           :log => 'thin.log',
+                           :pid => 'thin.pid',
+                           :onebyone => true,
+                           :wait => 30
+                          )
+  end
+  
+  it "should restart servers one by one" do
+    Command.should_receive(:run).with(:stop, options_for_port(3000))
+    Command.should_receive(:run).with(:start, options_for_port(3000))
+    @cluster.should_receive(:wait_until_server_started).with(3000)
+    
+    Command.should_receive(:run).with(:stop, options_for_port(3001))
+    Command.should_receive(:run).with(:start, options_for_port(3001))
+    @cluster.should_receive(:wait_until_server_started).with(3001)
+    
+    @cluster.restart
+  end
+  
+  private
+    def options_for_port(port)
+      { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
+    end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/controller_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/controller_spec.rb
new file mode 100644 (file)
index 0000000..d98a011
--- /dev/null
@@ -0,0 +1,129 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+require 'ostruct'
+include Controllers
+
+describe Controller, 'start' do
+  before do
+    @controller = Controller.new(:address              => '0.0.0.0',
+                                 :port                 => 3000,
+                                 :pid                  => 'thin.pid',
+                                 :log                  => 'thin.log',
+                                 :timeout              => 60,
+                                 :max_conns            => 2000,
+                                 :max_persistent_conns => 1000,
+                                 :adapter              => 'rails')
+    
+    @server = OpenStruct.new
+    @adapter = OpenStruct.new
+    
+    Server.should_receive(:new).with('0.0.0.0', 3000, @controller.options).and_return(@server)
+    @server.should_receive(:config)
+    Rack::Adapter::Rails.stub!(:new).and_return(@adapter)
+  end
+  
+  it "should configure server" do
+    @controller.start
+    
+    @server.app.should == @adapter
+    @server.pid_file.should == 'thin.pid'
+    @server.log_file.should == 'thin.log'
+    @server.maximum_connections.should == 2000
+    @server.maximum_persistent_connections.should == 1000
+  end
+  
+  it "should start as daemon" do
+    @controller.options[:daemonize] = true
+    @controller.options[:user] = true
+    @controller.options[:group] = true
+    
+    @server.should_receive(:daemonize)
+    @server.should_receive(:change_privilege)
+
+    @controller.start
+  end
+  
+  it "should configure Rails adapter" do
+    Rack::Adapter::Rails.should_receive(:new).with(@controller.options.merge(:root => nil))
+    
+    @controller.start
+  end
+  
+  it "should mount app under :prefix" do
+    @controller.options[:prefix] = '/app'
+    @controller.start
+    
+    @server.app.class.should == Rack::URLMap
+  end
+
+  it "should mount Stats adapter under :stats" do
+    @controller.options[:stats] = '/stats'
+    @controller.start
+    
+    @server.app.class.should == Stats::Adapter
+  end
+  
+  it "should load app from Rack config" do
+    @controller.options[:rackup] = File.dirname(__FILE__) + '/../../example/config.ru'
+    @controller.start
+    
+    @server.app.class.should == Proc
+  end
+
+  it "should load app from ruby file" do
+    @controller.options[:rackup] = File.dirname(__FILE__) + '/../../example/myapp.rb'
+    @controller.start
+    
+    @server.app.should == Myapp
+  end
+
+  it "should throwup if rackup is not a .ru or .rb file" do
+    proc do
+      @controller.options[:rackup] = File.dirname(__FILE__) + '/../../example/myapp.foo'
+      @controller.start
+    end.should raise_error(RuntimeError, /please/)
+  end
+  
+  it "should set server as threaded" do
+    @controller.options[:threaded] = true
+    @controller.start
+    
+    @server.threaded.should be_true
+  end
+  
+  it "should set RACK_ENV" do
+    @controller.options[:rackup] = File.dirname(__FILE__) + '/../../example/config.ru'
+    @controller.options[:environment] = "lolcat"
+    @controller.start
+    
+    ENV['RACK_ENV'].should == "lolcat"
+  end
+    
+end
+
+describe Controller do
+  before do
+    @controller = Controller.new(:pid => 'thin.pid', :timeout => 10)
+    @controller.stub!(:wait_for_file)
+  end
+  
+  it "should stop" do
+    Server.should_receive(:kill).with('thin.pid', 10)
+    @controller.stop
+  end
+  
+  it "should restart" do
+    Server.should_receive(:restart).with('thin.pid')
+    @controller.restart
+  end
+  
+  it "should write configuration file" do
+    silence_stream(STDOUT) do
+      Controller.new(:config => 'test.yml', :port => 5000, :address => '127.0.0.1').config
+    end
+
+    File.read('test.yml').should include('port: 5000', 'address: 127.0.0.1')
+    File.read('test.yml').should_not include('config: ')
+
+    File.delete('test.yml')
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/service_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/controllers/service_spec.rb
new file mode 100644 (file)
index 0000000..6c6cd3b
--- /dev/null
@@ -0,0 +1,50 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+include Controllers
+
+describe Service do
+  before(:all) do
+    silence_stream(STDERR) do
+      Service::INITD_PATH          = 'tmp/sandbox' + Service::INITD_PATH
+      Service::DEFAULT_CONFIG_PATH = 'tmp/sandbox' + Service::DEFAULT_CONFIG_PATH
+    end
+  end
+  
+  before do
+    Thin.stub!(:linux?).and_return(true)
+    FileUtils.mkdir_p 'tmp/sandbox'
+        
+    @service = Service.new(:all => 'spec/configs')
+  end
+  
+  it "should call command for each config file" do
+    Command.should_receive(:run).with(:start, :config => 'spec/configs/cluster.yml', :daemonize => true)
+    Command.should_receive(:run).with(:start, :config => 'spec/configs/single.yml', :daemonize => true)
+    
+    @service.start
+  end
+  
+  it "should create /etc/init.d/thin file when calling install" do
+    @service.install
+    
+    File.exist?(Service::INITD_PATH).should be_true
+    File.read(Service::INITD_PATH).should include('CONFIG_PATH=tmp/sandbox/etc/thin',
+                                                  'SCRIPT_NAME=tmp/sandbox/etc/init.d/thin',
+                                                  'DAEMON=' + Command.script)
+  end
+  
+  it "should create /etc/thin dir when calling install" do
+    @service.install
+    
+    File.directory?(Service::DEFAULT_CONFIG_PATH).should be_true
+  end
+  
+  it "should include specified path in /etc/init.d/thin script" do
+    @service.install('tmp/sandbox/usr/thin')
+    
+    File.read(Service::INITD_PATH).should include('CONFIG_PATH=tmp/sandbox/usr/thin')
+  end
+  
+  after do
+    FileUtils.rm_rf 'tmp/sandbox'
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/daemonizing_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/daemonizing_spec.rb
new file mode 100644 (file)
index 0000000..2cadd82
--- /dev/null
@@ -0,0 +1,196 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+class TestServer
+  include Logging # Daemonizable should include this?
+  include Daemonizable
+  
+  def stop
+  end
+  
+  def name
+    'Thin test server'
+  end
+end
+
+describe 'Daemonizing' do
+  before :all do
+    @logfile = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
+    @pidfile = 'test.pid'
+    File.delete(@logfile) if File.exist?(@logfile)
+    File.delete(@pidfile) if File.exist?(@pidfile)
+  end
+  
+  before :each do
+    @server = TestServer.new
+    @server.log_file = @logfile
+    @server.pid_file = @pidfile
+    @pid = nil
+  end
+  
+  it 'should have a pid file' do
+    @server.should respond_to(:pid_file)
+    @server.should respond_to(:pid_file=)
+  end
+  
+  it 'should create a pid file' do
+    @pid = fork do
+      @server.daemonize
+      sleep 1
+    end
+    
+    sleep 1
+    Process.wait(@pid)
+    File.exist?(@server.pid_file).should be_true
+    @pid = @server.pid
+
+    proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(5)
+  end
+  
+  it 'should redirect stdio to a log file' do
+    @pid = fork do
+      @server.log_file = 'daemon_test.log'
+      @server.daemonize
+
+      puts "simple puts"
+      STDERR.puts "STDERR.puts"
+      STDOUT.puts "STDOUT.puts"
+    end
+    Process.wait(@pid)
+    # Wait for the file to close and magical stuff to happen
+    proc { sleep 0.1 until File.exist?('daemon_test.log') }.should take_less_then(3)
+    sleep 0.5
+    
+    @pid = @server.pid
+
+    log = File.read('daemon_test.log')
+    log.should include('simple puts', 'STDERR.puts', 'STDOUT.puts')
+    
+    File.delete 'daemon_test.log'
+  end
+  
+  it 'should change privilege' do
+    @pid = fork do
+      @server.daemonize
+      @server.change_privilege('root', 'admin')
+    end
+    Process.wait(@pid)
+    $?.should be_a_success
+  end
+  
+  it 'should kill process in pid file' do
+    @pid = fork do
+      @server.daemonize
+      loop { sleep 3 }
+    end
+  
+    server_should_start_in_less_then 3
+    
+    @pid = @server.pid
+  
+    silence_stream STDOUT do
+      TestServer.kill(@server.pid_file, 1)
+    end
+  
+    File.exist?(@server.pid_file).should be_false
+  end
+  
+  it 'should force kill process in pid file' do
+    @pid = fork do
+      @server.daemonize
+      loop { sleep 3 }
+    end
+  
+    server_should_start_in_less_then 3
+    
+    @pid = @server.pid
+  
+    silence_stream STDOUT do
+      TestServer.kill(@server.pid_file, 0)
+    end
+  
+    File.exist?(@server.pid_file).should be_false
+  end
+  
+  it 'should send kill signal if timeout' do
+    @pid = fork do
+      @server.should_receive(:stop) # pretend we cannot handle the INT signal
+      @server.daemonize
+      sleep 5
+    end
+  
+    server_should_start_in_less_then 10
+    
+    @pid = @server.pid
+  
+    silence_stream STDOUT do
+      TestServer.kill(@server.pid_file, 1)
+    end
+    
+    sleep 1
+  
+    File.exist?(@server.pid_file).should be_false
+    Process.running?(@pid).should be_false
+  end
+  
+  it "should restart" do
+    @pid = fork do
+      @server.on_restart {}
+      @server.daemonize
+      sleep 5
+    end
+    
+    server_should_start_in_less_then 10
+    
+    @pid = @server.pid
+  
+    silence_stream STDOUT do
+      TestServer.restart(@server.pid_file)
+    end
+    
+    proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(10)
+  end
+  
+  it "should ignore if no restart block specified" do
+    TestServer.restart(@server.pid_file)
+  end
+  
+  it "should not restart when not running" do
+    silence_stream STDOUT do
+      TestServer.restart(@server.pid_file)
+    end
+  end
+  
+  it "should exit and raise if pid file already exist" do
+    @pid = fork do
+      @server.daemonize
+      sleep 5
+    end
+    server_should_start_in_less_then 10
+    
+    @pid = @server.pid
+
+    proc { @server.daemonize }.should raise_error(PidFileExist)
+    
+    File.exist?(@server.pid_file).should be_true
+  end
+  
+  it "should should delete pid file if stale" do
+    # Create a file w/ a PID that does not exist
+    File.open(@server.pid_file, 'w') { |f| f << 999999999 }
+    
+    @server.send(:remove_stale_pid_file)
+    
+    File.exist?(@server.pid_file).should be_false
+  end
+  
+  after do
+    Process.kill(9, @pid.to_i) if @pid && Process.running?(@pid.to_i)
+    Process.kill(9, @server.pid) if @server.pid && Process.running?(@server.pid)
+    File.delete(@server.pid_file) rescue nil
+  end
+  
+  private
+    def server_should_start_in_less_then(sec=10)
+      proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
+    end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/headers_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/headers_spec.rb
new file mode 100644 (file)
index 0000000..98ee470
--- /dev/null
@@ -0,0 +1,40 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Headers do
+  before do
+    @headers = Headers.new
+  end
+  
+  it 'should allow duplicate on some fields' do
+    @headers['Set-Cookie'] = 'twice'
+    @headers['Set-Cookie'] = 'is cooler the once'
+    
+    @headers.to_s.should == "Set-Cookie: twice\r\nSet-Cookie: is cooler the once\r\n"
+  end
+  
+  it 'should overwrite value on non duplicate fields' do
+    @headers['Host'] = 'this is unique'
+    @headers['Host'] = 'so is this'
+
+    @headers.to_s.should == "Host: this is unique\r\n"
+  end
+  
+  it 'should output to string' do
+    @headers['Host'] = 'localhost:3000'
+    @headers['Set-Cookie'] = 'twice'
+    @headers['Set-Cookie'] = 'is cooler the once'
+    
+    @headers.to_s.should == "Host: localhost:3000\r\nSet-Cookie: twice\r\nSet-Cookie: is cooler the once\r\n"
+  end
+
+  it 'should ignore nil values' do
+    @headers['Something'] = nil
+    @headers.to_s.should_not include('Something: ')
+  end
+
+  it 'should format Time values correctly' do
+    time = Time.now
+    @headers['Modified-At'] = time
+    @headers.to_s.should include("Modified-At: #{time.httpdate}")
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/logging_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/logging_spec.rb
new file mode 100644 (file)
index 0000000..eeb160e
--- /dev/null
@@ -0,0 +1,46 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+class TestLogging
+  include Logging
+end
+
+describe Logging do
+  before do
+    Logging.silent = false
+    @object = TestLogging.new
+  end
+  
+  it "should output debug when set to true" do
+    Logging.debug = true
+    @object.should_receive(:puts)
+    @object.debug 'hi'
+  end
+
+  it "should output trace when set to true" do
+    Logging.trace = true
+    @object.should_receive(:puts)
+    @object.trace 'hi'
+  end
+
+  it "should not output when silenced" do
+    Logging.silent = true
+    @object.should_not_receive(:puts)
+    @object.log 'hi'
+  end
+  
+  it "should not output when silenced as instance method" do
+    @object.silent = true
+    
+    @object.should_not_receive(:puts)
+    @object.log 'hi'
+  end
+  
+  it "should be usable as module functions" do
+    Logging.silent = true
+    Logging.log "hi"
+  end
+  
+  after do
+    Logging.silent = true
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/request_perf_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/request_perf_spec.rb
new file mode 100644 (file)
index 0000000..bd1e6a4
--- /dev/null
@@ -0,0 +1,50 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Request, 'performance' do
+  it "should be faster then #{max_parsing_time = 0.0002} RubySeconds" do
+    body = <<-EOS.chomp.gsub("\n", "\r\n")
+POST /postit HTTP/1.1
+Host: localhost:3000
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
+Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Keep-Alive: 300
+Connection: keep-alive
+Content-Type: text/html
+Content-Length: 37
+
+hi=there&name=marc&email=macournoyer@gmail.com
+EOS
+
+    proc { R(body) }.should be_faster_then(max_parsing_time)
+  end
+
+  it 'should be comparable to Mongrel parser' do
+    require 'http11'
+
+    body = <<-EOS.chomp.gsub("\n", "\r\n")
+POST /postit HTTP/1.1
+Host: localhost:3000
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
+Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Keep-Alive: 300
+Connection: keep-alive
+Content-Type: text/html
+Content-Length: 37
+
+hi=there&name=marc&email=macournoyer@gmail.com
+EOS
+
+    tests = 10_000
+    puts
+    Benchmark.bmbm(10) do |results|
+      results.report("mongrel:") { tests.times { Mongrel::HttpParser.new.execute({}, body.dup, 0) } }
+      results.report("thin:") { tests.times { Thin::HttpParser.new.execute({'rack.input' => StringIO.new}, body.dup, 0) } }
+    end
+  end if ENV['BM']
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/response_perf_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/response_perf_spec.rb
new file mode 100644 (file)
index 0000000..1456cae
--- /dev/null
@@ -0,0 +1,19 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Response, 'performance' do
+  before do
+    @response = Response.new
+    @response.body = ''
+  end
+  
+  it "should be fast" do
+    @response.body << <<-EOS
+<html><head><title>Dir listing</title></head>
+<body><h1>Listing stuff</h1><ul>
+#{'<li>Hi!</li>' * 100}
+</ul></body></html>
+EOS
+
+    proc { @response.each { |l| l } }.should be_faster_then(0.00011)
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/server_perf_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/perf/server_perf_spec.rb
new file mode 100644 (file)
index 0000000..528ebc4
--- /dev/null
@@ -0,0 +1,39 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, 'performance' do
+  before do
+    start_server do |env|
+      body = env.inspect + env['rack.input'].read
+      [200, { 'Content-Length' => body.size.to_s }, body]
+    end
+  end
+  
+  it "should handle GET in less then #{get_request_time = 0.0045} RubySecond" do
+    proc { get('/') }.should be_faster_then(get_request_time)
+  end
+  
+  it "should handle POST in less then #{post_request_time = 0.007} RubySecond" do
+    proc { post('/', :file => 'X' * 1000) }.should be_faster_then(post_request_time)
+  end
+  
+  after do
+    stop_server
+  end
+end
+
+describe Server, 'UNIX socket performance' do
+  before do
+    start_server('/tmp/thin_test.sock') do |env|
+      body = env.inspect + env['rack.input'].read
+      [200, { 'Content-Length' => body.size.to_s }, body]
+    end
+  end
+  
+  it "should handle GET in less then #{get_request_time = 0.002} RubySecond" do
+    proc { get('/') }.should be_faster_then(get_request_time)
+  end
+  
+  after do
+    stop_server
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/loader_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/loader_spec.rb
new file mode 100644 (file)
index 0000000..6706023
--- /dev/null
@@ -0,0 +1,42 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Rack::Adapter do
+  before do
+    @config_ru_path = File.dirname(__FILE__) + '/../../example'
+    @rails_path = File.dirname(__FILE__) + '/../rails_app'
+  end
+  
+  it "should load Rack app from config" do
+    Rack::Adapter.load(@config_ru_path + '/config.ru').class.should == Proc
+  end
+  
+  it "should guess Rack app from dir" do
+    Rack::Adapter.guess(@config_ru_path).should == :rack
+  end
+  
+  it "should guess rails app from dir" do
+    Rack::Adapter.guess(@rails_path).should == :rails
+  end
+  
+  it "should return nil when can't guess from dir" do
+    proc { Rack::Adapter.guess('.') }.should raise_error(Rack::AdapterNotFound)
+  end
+  
+  it "should load Rack adapter" do
+    Rack::Adapter.for(:rack, :chdir => @config_ru_path).class.should == Proc
+  end
+  
+  it "should load Rails adapter" do
+    Rack::Adapter::Rails.should_receive(:new)
+    Rack::Adapter.for(:rails, :chdir => @rails_path)
+  end
+  
+  it "should load File adapter" do
+    Rack::File.should_receive(:new)
+    Rack::Adapter.for(:file)
+  end
+  
+  it "should raise error when adapter can't be found" do
+    proc { Rack::Adapter.for(:fart, {}) }.should raise_error(Rack::AdapterNotFound)
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/rails_adapter_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rack/rails_adapter_spec.rb
new file mode 100644 (file)
index 0000000..e7fcfb9
--- /dev/null
@@ -0,0 +1,173 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+require 'rack/mock'
+
+begin
+  gem 'rails', '= 2.0.2' # We could freeze Rails in the rails_app dir to remove this
+
+  describe Rack::Adapter::Rails do
+    before do
+      @rails_app_path = File.dirname(__FILE__) + '/../rails_app'
+      @request = Rack::MockRequest.new(Rack::Adapter::Rails.new(:root => @rails_app_path))
+    end
+    
+    it "should handle simple GET request" do
+      res = @request.get("/simple", :lint => true)
+
+      res.should be_ok
+      res["Content-Type"].should include("text/html")
+
+      res.body.should include('Simple#index')
+    end
+
+    it "should handle POST parameters" do
+      data = "foo=bar"
+      res = @request.post("/simple/post_form", :input => data, 'CONTENT_LENGTH' => data.size.to_s, :lint => true)
+
+      res.should be_ok
+      res["Content-Type"].should include("text/html")
+      res["Content-Length"].should_not be_nil
+    
+      res.body.should include('foo: bar')
+    end
+  
+    it "should serve static files" do
+      res = @request.get("/index.html", :lint => true)
+
+      res.should be_ok
+      res["Content-Type"].should include("text/html")
+    end
+    
+    it "should serve root with index.html if present" do
+      res = @request.get("/", :lint => true)
+
+      res.should be_ok
+      res["Content-Length"].to_i.should == File.size(@rails_app_path + '/public/index.html')
+    end
+    
+    it "should serve page cache if present" do
+      res = @request.get("/simple/cached?value=cached", :lint => true)
+
+      res.should be_ok
+      res.body.should == 'cached'
+      
+      res = @request.get("/simple/cached?value=notcached")
+      
+      res.should be_ok
+      res.body.should == 'cached'
+    end
+    
+    it "should not serve page cache on POST request" do
+      res = @request.get("/simple/cached?value=cached", :lint => true)
+
+      res.should be_ok
+      res.body.should == 'cached'
+      
+      res = @request.post("/simple/cached?value=notcached")
+      
+      res.should be_ok
+      res.body.should == 'notcached'
+    end
+    
+    it "handles multiple cookies" do
+      res = @request.get('/simple/set_cookie?name=a&value=1', :lint => true)
+    
+      res.should be_ok
+      res.original_headers['Set-Cookie'].size.should == 2
+      res.original_headers['Set-Cookie'].first.should include('a=1; path=/')
+      res.original_headers['Set-Cookie'].last.should include('_rails_app_session')
+    end
+    
+    after do
+      FileUtils.rm_rf @rails_app_path + '/public/simple'
+    end
+  end
+  
+  describe Rack::Adapter::Rails, 'with prefix' do
+    before do
+      @rails_app_path = File.dirname(__FILE__) + '/../rails_app'
+      @prefix = '/nowhere'
+      @request = Rack::MockRequest.new(
+        Rack::URLMap.new(
+          @prefix => Rack::Adapter::Rails.new(:root => @rails_app_path, :prefix => @prefix)))
+    end
+  
+    it "should handle simple GET request" do
+      res = @request.get("#{@prefix}/simple", :lint => true)
+
+      res.should be_ok
+      res["Content-Type"].should include("text/html")
+
+      res.body.should include('Simple#index')
+    end
+  end
+
+rescue Gem::LoadError
+  warn 'Rails 2.0.2 is required to run the Rails adapter specs'
+end
+
+module RailsMock
+  module VERSION
+    MAJOR = 0
+    MINOR = 0
+    TINY = 0
+  end
+end
+
+describe Rack::Adapter::Rails, "Adapter version" do
+  before do
+    unless defined?(::Rails)
+      ::Rails = RailsMock
+    end
+  end
+  
+  it "should use Rack based adapter when Rails = 2.2.3" do
+    with_rails_version(2, 2, 3) do
+      Rack::Adapter::Rails.should be_rack_based
+    end
+  end
+
+  it "should not use Rack based adapter when Rails < 2.2.3" do
+    with_rails_version(2, 2, 2) do
+      Rack::Adapter::Rails.should_not be_rack_based
+    end
+  end
+
+  it "should not use Rack based adapter when Rails = 1.2.3" do
+    with_rails_version(1, 2, 2) do
+      Rack::Adapter::Rails.should_not be_rack_based
+    end
+  end
+  
+  it "should use Rack based adapter when Rails = 3.0.0" do
+    with_rails_version(3, 0, 0) do
+      Rack::Adapter::Rails.should be_rack_based
+    end
+  end
+  
+  def with_rails_version(major, minor, tiny)
+    old_major = ::Rails::VERSION::MAJOR
+    old_minor = ::Rails::VERSION::MINOR
+    old_tiny = ::Rails::VERSION::TINY
+    
+    silence_warnings do
+      ::Rails::VERSION.const_set :MAJOR, major
+      ::Rails::VERSION.const_set :MINOR, minor
+      ::Rails::VERSION.const_set :TINY, tiny
+    end
+    
+    yield
+    
+    silence_warnings do
+      ::Rails::VERSION.const_set :MAJOR, old_major
+      ::Rails::VERSION.const_set :MINOR, old_minor
+      ::Rails::VERSION.const_set :TINY, old_tiny
+    end
+  end
+  
+  def silence_warnings
+    old_verbose, $VERBOSE = $VERBOSE, nil
+    yield
+  ensure
+    $VERBOSE = old_verbose
+  end unless method_defined?(:silence_warnings)
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/application.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/application.rb
new file mode 100644 (file)
index 0000000..cfdb724
--- /dev/null
@@ -0,0 +1,10 @@
+# Filters added to this controller apply to all controllers in the application.
+# Likewise, all the methods added will be available for all controllers.
+
+class ApplicationController < ActionController::Base
+  helper :all # include all helpers, all the time
+
+  # See ActionController::RequestForgeryProtection for details
+  # Uncomment the :secret if you're not using the cookie session store
+  # protect_from_forgery # :secret => 'a8af303b8dabf2d2d8f1a7912ac04d7d'
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/simple_controller.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/controllers/simple_controller.rb
new file mode 100644 (file)
index 0000000..2c17427
--- /dev/null
@@ -0,0 +1,19 @@
+class SimpleController < ApplicationController
+  caches_page :cached
+  
+  def index
+  end
+  
+  def post_form
+    render :text => params.to_yaml
+  end
+  
+  def set_cookie
+    cookies[params[:name]] = params[:value] if params[:name]
+    render :text => cookies.to_yaml
+  end
+  
+  def cached
+    render :text => params[:value]
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/helpers/application_helper.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/helpers/application_helper.rb
new file mode 100644 (file)
index 0000000..22a7940
--- /dev/null
@@ -0,0 +1,3 @@
+# Methods added to this helper will be available to all templates in the application.
+module ApplicationHelper
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/views/simple/index.html.erb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/app/views/simple/index.html.erb
new file mode 100644 (file)
index 0000000..7717b8e
--- /dev/null
@@ -0,0 +1,15 @@
+<h1>Simple#index</h1>
+
+<h2>ENV</h2>
+<%= request.env.to_yaml %>
+
+<h2>Cookies</h2>
+<%= request.cookies.to_yaml %>
+
+<h2>Params</h2>
+<%= params.to_yaml %>
+
+<% form_tag '/simple' do %>
+  <%= text_field_tag :a %>
+  <%= submit_tag 'Submit' %>
+<% end %>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/boot.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/boot.rb
new file mode 100644 (file)
index 0000000..5697cc1
--- /dev/null
@@ -0,0 +1,109 @@
+# Don't change this file!
+# Configure your app in config/environment.rb and config/environments/*.rb
+
+RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
+
+module Rails
+  class << self
+    def boot!
+      unless booted?
+        preinitialize
+        pick_boot.run
+      end
+    end
+
+    def booted?
+      defined? Rails::Initializer
+    end
+
+    def pick_boot
+      (vendor_rails? ? VendorBoot : GemBoot).new
+    end
+
+    def vendor_rails?
+      File.exist?("#{RAILS_ROOT}/vendor/rails")
+    end
+
+    # FIXME : Ruby 1.9
+    def preinitialize
+      load(preinitializer_path) if File.exists?(preinitializer_path)
+    end
+
+    def preinitializer_path
+      "#{RAILS_ROOT}/config/preinitializer.rb"
+    end
+  end
+
+  class Boot
+    def run
+      load_initializer
+      Rails::Initializer.run(:set_load_path)
+    end
+  end
+
+  class VendorBoot < Boot
+    def load_initializer
+      require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
+    end
+  end
+
+  class GemBoot < Boot
+    def load_initializer
+      self.class.load_rubygems
+      load_rails_gem
+      require 'initializer'
+    end
+
+    def load_rails_gem
+      if version = self.class.gem_version
+        gem 'rails', version
+      else
+        gem 'rails'
+      end
+    rescue Gem::LoadError => load_error
+      $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
+      exit 1
+    end
+
+    class << self
+      def rubygems_version
+        Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
+      end
+
+      def gem_version
+        if defined? RAILS_GEM_VERSION
+          RAILS_GEM_VERSION
+        elsif ENV.include?('RAILS_GEM_VERSION')
+          ENV['RAILS_GEM_VERSION']
+        else
+          parse_gem_version(read_environment_rb)
+        end
+      end
+
+      def load_rubygems
+        require 'rubygems'
+
+        unless rubygems_version >= '0.9.4'
+          $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
+          exit 1
+        end
+
+      rescue LoadError
+        $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
+        exit 1
+      end
+
+      def parse_gem_version(text)
+        $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
+      end
+
+      private
+        def read_environment_rb
+          File.read("#{RAILS_ROOT}/config/environment.rb")
+        end
+    end
+  end
+end
+
+# All that for this:
+Rails.boot!
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environment.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environment.rb
new file mode 100644 (file)
index 0000000..2af27f1
--- /dev/null
@@ -0,0 +1,64 @@
+# Be sure to restart your server when you modify this file
+
+# Uncomment below to force Rails into production mode when
+# you don't control web/app server and can't set it the proper way
+# ENV['RAILS_ENV'] ||= 'production'
+
+# Specifies gem version of Rails to use when vendor/rails is not present
+RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION
+
+# Bootstrap the Rails environment, frameworks, and default configuration
+require File.join(File.dirname(__FILE__), 'boot')
+
+Rails::Initializer.run do |config|
+  # Settings in config/environments/* take precedence over those specified here.
+  # Application configuration should go into files in config/initializers
+  # -- all .rb files in that directory are automatically loaded.
+  # See Rails::Configuration for more options.
+
+  # Skip frameworks you're not going to use (only works if using vendor/rails).
+  # To use Rails without a database, you must remove the Active Record framework
+  config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
+
+  # Only load the plugins named here, in the order given. By default, all plugins 
+  # in vendor/plugins are loaded in alphabetical order.
+  # :all can be used as a placeholder for all plugins not explicitly named
+  # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+  # Add additional load paths for your own custom dirs
+  # config.load_paths += %W( #{RAILS_ROOT}/extras )
+  
+  # No need for log files
+  config.logger = Logger.new(nil)
+
+  # Force all environments to use the same logger level
+  # (by default production uses :info, the others :debug)
+  # config.log_level = :debug
+
+  # Your secret key for verifying cookie session data integrity.
+  # If you change this key, all old sessions will become invalid!
+  # Make sure the secret is at least 30 characters and all random, 
+  # no regular words or you'll be exposed to dictionary attacks.
+  config.action_controller.session = {
+    :session_key => '_rails_app_session',
+    :secret      => 'cb7141365b4443eff37e7122473e704ceae95146a4028930b21300965fe6abec51e3e93b2670a914b3b65d06058b81aadfe6b240d63e7d7713db044b42a6e1c1'
+  }
+  
+  config.action_controller.allow_forgery_protection = false
+
+  # Use the database for sessions instead of the cookie-based default,
+  # which shouldn't be used to store highly confidential information
+  # (create the session table with 'rake db:sessions:create')
+  # config.action_controller.session_store = :active_record_store
+
+  # Use SQL instead of Active Record's schema dumper when creating the test database.
+  # This is necessary if your schema can't be completely dumped by the schema dumper,
+  # like if you have constraints or database-specific column types
+  # config.active_record.schema_format = :sql
+
+  # Activate observers that should always be running
+  # config.active_record.observers = :cacher, :garbage_collector
+
+  # Make Active Record use UTC-base instead of local time
+  # config.active_record.default_timezone = :utc
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/development.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/development.rb
new file mode 100644 (file)
index 0000000..191f39c
--- /dev/null
@@ -0,0 +1,18 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request.  This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_view.debug_rjs                         = true
+config.action_controller.perform_caching             = true
+config.action_view.cache_template_extensions         = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/production.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/production.rb
new file mode 100644 (file)
index 0000000..91f541c
--- /dev/null
@@ -0,0 +1,19 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The production environment is meant for finished, "live" apps.
+# Code is not reloaded between requests
+config.cache_classes = true
+
+# Use a different logger for distributed setups
+# config.logger = SyslogLogger.new
+
+# Full error reports are disabled and caching is turned on
+config.action_controller.consider_all_requests_local = false
+config.action_controller.perform_caching             = true
+config.action_view.cache_template_loading            = true
+
+# Enable serving of images, stylesheets, and javascripts from an asset server
+# config.action_controller.asset_host                  = "http://assets.example.com"
+
+# Disable delivery errors, bad email addresses will be ignored
+# config.action_mailer.raise_delivery_errors = false
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/test.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/environments/test.rb
new file mode 100644 (file)
index 0000000..58850a7
--- /dev/null
@@ -0,0 +1,22 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Disable request forgery protection in test environment
+config.action_controller.allow_forgery_protection    = false
+
+# Tell ActionMailer not to deliver emails to the real world.
+# The :test delivery method accumulates sent emails in the
+# ActionMailer::Base.deliveries array.
+config.action_mailer.delivery_method = :test
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/inflections.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/inflections.rb
new file mode 100644 (file)
index 0000000..09158b8
--- /dev/null
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format 
+# (all these examples are active by default):
+# Inflector.inflections do |inflect|
+#   inflect.plural /^(ox)$/i, '\1en'
+#   inflect.singular /^(ox)en/i, '\1'
+#   inflect.irregular 'person', 'people'
+#   inflect.uncountable %w( fish sheep )
+# end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/mime_types.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/initializers/mime_types.rb
new file mode 100644 (file)
index 0000000..72aca7e
--- /dev/null
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/routes.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/config/routes.rb
new file mode 100644 (file)
index 0000000..d94afa1
--- /dev/null
@@ -0,0 +1,35 @@
+ActionController::Routing::Routes.draw do |map|
+  # The priority is based upon order of creation: first created -> highest priority.
+
+  # Sample of regular route:
+  #   map.connect 'products/:id', :controller => 'catalog', :action => 'view'
+  # Keep in mind you can assign values other than :controller and :action
+
+  # Sample of named route:
+  #   map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
+  # This route can be invoked with purchase_url(:id => product.id)
+
+  # Sample resource route (maps HTTP verbs to controller actions automatically):
+  #   map.resources :products
+
+  # Sample resource route with options:
+  #   map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
+
+  # Sample resource route with sub-resources:
+  #   map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
+
+  # Sample resource route within a namespace:
+  #   map.namespace :admin do |admin|
+  #     # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
+  #     admin.resources :products
+  #   end
+
+  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
+  # map.root :controller => "welcome"
+
+  # See how all your routes lay out with "rake routes"
+
+  # Install the default routes as the lowest priority.
+  map.connect ':controller/:action/:id'
+  map.connect ':controller/:action/:id.:format'
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/404.html b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/404.html
new file mode 100644 (file)
index 0000000..eff660b
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>The page you were looking for doesn't exist (404)</title>
+       <style type="text/css">
+               body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+               div.dialog {
+                       width: 25em;
+                       padding: 0 4em;
+                       margin: 4em auto 0 auto;
+                       border: 1px solid #ccc;
+                       border-right-color: #999;
+                       border-bottom-color: #999;
+               }
+               h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+       </style>
+</head>
+
+<body>
+  <!-- This file lives in public/404.html -->
+  <div class="dialog">
+    <h1>The page you were looking for doesn't exist.</h1>
+    <p>You may have mistyped the address or the page may have moved.</p>
+  </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/422.html b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/422.html
new file mode 100644 (file)
index 0000000..b54e4a3
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>The change you wanted was rejected (422)</title>
+       <style type="text/css">
+               body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+               div.dialog {
+                       width: 25em;
+                       padding: 0 4em;
+                       margin: 4em auto 0 auto;
+                       border: 1px solid #ccc;
+                       border-right-color: #999;
+                       border-bottom-color: #999;
+               }
+               h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+       </style>
+</head>
+
+<body>
+  <!-- This file lives in public/422.html -->
+  <div class="dialog">
+    <h1>The change you wanted was rejected.</h1>
+    <p>Maybe you tried to change something you didn't have access to.</p>
+  </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/500.html b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/500.html
new file mode 100644 (file)
index 0000000..0e9c14f
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>We're sorry, but something went wrong (500)</title>
+       <style type="text/css">
+               body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+               div.dialog {
+                       width: 25em;
+                       padding: 0 4em;
+                       margin: 4em auto 0 auto;
+                       border: 1px solid #ccc;
+                       border-right-color: #999;
+                       border-bottom-color: #999;
+               }
+               h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+       </style>
+</head>
+
+<body>
+  <!-- This file lives in public/500.html -->
+  <div class="dialog">
+    <h1>We're sorry, but something went wrong.</h1>
+    <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+  </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.cgi b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.cgi
new file mode 100644 (file)
index 0000000..32fa3b2
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.fcgi b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.fcgi
new file mode 100644 (file)
index 0000000..664dbbb
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# You may specify the path to the FastCGI crash log (a log of unhandled
+# exceptions which forced the FastCGI instance to exit, great for debugging)
+# and the number of requests to process before running garbage collection.
+#
+# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
+# and the GC period is nil (turned off).  A reasonable number of requests
+# could range from 10-100 depending on the memory footprint of your app.
+#
+# Example:
+#   # Default log path, normal GC behavior.
+#   RailsFCGIHandler.process!
+#
+#   # Default log path, 50 requests between GC.
+#   RailsFCGIHandler.process! nil, 50
+#
+#   # Custom log path, normal GC behavior.
+#   RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
+#
+require File.dirname(__FILE__) + "/../config/environment"
+require 'fcgi_handler'
+
+RailsFCGIHandler.process!
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/dispatch.rb
new file mode 100644 (file)
index 0000000..32fa3b2
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/favicon.ico b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/favicon.ico
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/images/rails.png b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/images/rails.png
new file mode 100644 (file)
index 0000000..b8441f1
Binary files /dev/null and b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/images/rails.png differ
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/index.html b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/index.html
new file mode 100644 (file)
index 0000000..84b7b57
--- /dev/null
@@ -0,0 +1,277 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+  <head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <title>Ruby on Rails: Welcome aboard</title>
+    <style type="text/css" media="screen">
+      body {
+        margin: 0;
+        margin-bottom: 25px;
+        padding: 0;
+        background-color: #f0f0f0;
+        font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
+        font-size: 13px;
+        color: #333;
+      }
+      
+      h1 {
+        font-size: 28px;
+        color: #000;
+      }
+      
+      a  {color: #03c}
+      a:hover {
+        background-color: #03c;
+        color: white;
+        text-decoration: none;
+      }
+      
+      
+      #page {
+        background-color: #f0f0f0;
+        width: 750px;
+        margin: 0;
+        margin-left: auto;
+        margin-right: auto;
+      }
+      
+      #content {
+        float: left;
+        background-color: white;
+        border: 3px solid #aaa;
+        border-top: none;
+        padding: 25px;
+        width: 500px;
+      }
+      
+      #sidebar {
+        float: right;
+        width: 175px;
+      }
+
+      #footer {
+        clear: both;
+      }
+      
+
+      #header, #about, #getting-started {
+        padding-left: 75px;
+        padding-right: 30px;
+      }
+
+
+      #header {
+        background-image: url("images/rails.png");
+        background-repeat: no-repeat;
+        background-position: top left;
+        height: 64px;
+      }
+      #header h1, #header h2 {margin: 0}
+      #header h2 {
+        color: #888;
+        font-weight: normal;
+        font-size: 16px;
+      }
+      
+      
+      #about h3 {
+        margin: 0;
+        margin-bottom: 10px;
+        font-size: 14px;
+      }
+      
+      #about-content {
+        background-color: #ffd;
+        border: 1px solid #fc0;
+        margin-left: -11px;
+      }
+      #about-content table {
+        margin-top: 10px;
+        margin-bottom: 10px;
+        font-size: 11px;
+        border-collapse: collapse;
+      }
+      #about-content td {
+        padding: 10px;
+        padding-top: 3px;
+        padding-bottom: 3px;
+      }
+      #about-content td.name  {color: #555}
+      #about-content td.value {color: #000}
+      
+      #about-content.failure {
+        background-color: #fcc;
+        border: 1px solid #f00;
+      }
+      #about-content.failure p {
+        margin: 0;
+        padding: 10px;
+      }
+      
+      
+      #getting-started {
+        border-top: 1px solid #ccc;
+        margin-top: 25px;
+        padding-top: 15px;
+      }
+      #getting-started h1 {
+        margin: 0;
+        font-size: 20px;
+      }
+      #getting-started h2 {
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        margin-bottom: 25px;
+      }
+      #getting-started ol {
+        margin-left: 0;
+        padding-left: 0;
+      }
+      #getting-started li {
+        font-size: 18px;
+        color: #888;
+        margin-bottom: 25px;
+      }
+      #getting-started li h2 {
+        margin: 0;
+        font-weight: normal;
+        font-size: 18px;
+        color: #333;
+      }
+      #getting-started li p {
+        color: #555;
+        font-size: 13px;
+      }
+      
+      
+      #search {
+        margin: 0;
+        padding-top: 10px;
+        padding-bottom: 10px;
+        font-size: 11px;
+      }
+      #search input {
+        font-size: 11px;
+        margin: 2px;
+      }
+      #search-text {width: 170px}
+      
+      
+      #sidebar ul {
+        margin-left: 0;
+        padding-left: 0;
+      }
+      #sidebar ul h3 {
+        margin-top: 25px;
+        font-size: 16px;
+        padding-bottom: 10px;
+        border-bottom: 1px solid #ccc;
+      }
+      #sidebar li {
+        list-style-type: none;
+      }
+      #sidebar ul.links li {
+        margin-bottom: 5px;
+      }
+      
+    </style>
+    <script type="text/javascript" src="javascripts/prototype.js"></script>
+    <script type="text/javascript" src="javascripts/effects.js"></script>
+    <script type="text/javascript">
+      function about() {
+        if (Element.empty('about-content')) {
+          new Ajax.Updater('about-content', 'rails/info/properties', {
+            method:     'get',
+            onFailure:  function() {Element.classNames('about-content').add('failure')},
+            onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
+          });
+        } else {
+          new Effect[Element.visible('about-content') ? 
+            'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
+        }
+      }
+      
+      window.onload = function() {
+        $('search-text').value = '';
+        $('search').onsubmit = function() {
+          $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
+        }
+      }
+    </script>
+  </head>
+  <body>
+    <div id="page">
+      <div id="sidebar">
+        <ul id="sidebar-items">
+          <li>
+            <form id="search" action="http://www.google.com/search" method="get">
+              <input type="hidden" name="hl" value="en" />
+              <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
+              <input type="submit" value="Search" /> the Rails site
+            </form>
+          </li>
+        
+          <li>
+            <h3>Join the community</h3>
+            <ul class="links">
+              <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
+              <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
+              <li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
+              <li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
+              <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
+              <li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
+            </ul>
+          </li>
+          
+          <li>
+            <h3>Browse the documentation</h3>
+            <ul class="links">
+              <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
+              <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
+              <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
+            </ul>
+          </li>
+        </ul>
+      </div>
+
+      <div id="content">
+        <div id="header">
+          <h1>Welcome aboard</h1>
+          <h2>You&rsquo;re riding Ruby on Rails!</h2>
+        </div>
+
+        <div id="about">
+          <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
+          <div id="about-content" style="display: none"></div>
+        </div>
+        
+        <div id="getting-started">
+          <h1>Getting started</h1>
+          <h2>Here&rsquo;s how to get rolling:</h2>
+          
+          <ol>
+            <li>
+              <h2>Create your databases and edit <tt>config/database.yml</tt></h2>
+              <p>Rails needs to know your login and password.</p>
+            </li>
+          
+            <li>
+              <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
+              <p>To see all available options, run it without parameters.</p>
+            </li>
+            
+            <li>
+              <h2>Set up a default route and remove or rename this file</h2>
+              <p>Routes are set up in config/routes.rb.</p>
+            </li>
+          </ol>
+        </div>
+      </div>
+      
+      <div id="footer">&nbsp;</div>
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/application.js b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/application.js
new file mode 100644 (file)
index 0000000..fe45776
--- /dev/null
@@ -0,0 +1,2 @@
+// Place your application-specific JavaScript functions and classes here
+// This file is automatically included by javascript_include_tag :defaults
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/controls.js b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/controls.js
new file mode 100644 (file)
index 0000000..fbc4418
--- /dev/null
@@ -0,0 +1,963 @@
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { }
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element)
+    this.element     = element; 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+      function(element, update){ 
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide || 
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount = 
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+    
+    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+    
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw 'Server returned an invalid collection representation.';
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/dragdrop.js b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/dragdrop.js
new file mode 100644 (file)
index 0000000..ccf4a1e
--- /dev/null
@@ -0,0 +1,972 @@
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+    
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event); 
+        return true; 
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
+      },
+      zindex: 1000,
+      revert: false,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || { });
+
+    this.element = $(element);
+    
+    if(options.handle && Object.isString(options.handle))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+        
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this.element._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+        
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+    
+    Draggables.notify('onDrag', this, event);
+    
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+    
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this.element._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this.element._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false; 
+    if(success) { 
+      dropped = Droppables.fire(event, this.element); 
+      if (!dropped) dropped = false; 
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+      
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
+  sortables: { },
+  
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      delay:       0,
+      hoverclass:  null,
+      ghosting:    false,
+      quiet:       false, 
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+      
+      // these take arrays of elements or ids and can be 
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+      
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e); 
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+    
+    Sortable._marker.show();
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+    
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+  if (child.parentNode == element) return true;
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {   
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/effects.js b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/effects.js
new file mode 100644 (file)
index 0000000..65aed23
--- /dev/null
@@ -0,0 +1,1120 @@
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if (this.slice(0,1) == '#') {  
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if (this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return (color.length==7 ? color : (arguments[0] || this));  
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  element.setStyle({fontSize: (percent/100) + 'em'});   
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  Transitions: {
+    linear: Prototype.K,
+    sinoidal: function(pos) {
+      return (-Math.cos(pos*Math.PI)/2) + 0.5;
+    },
+    reverse: function(pos) {
+      return 1-pos;
+    },
+    flicker: function(pos) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+    },
+    pulse: function(pos, pulses) { 
+      pulses = pulses || 5; 
+      return (
+        ((pos % (1/pulses)) * pulses).round() == 0 ? 
+              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
+          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+        );
+    },
+    spring: function(pos) { 
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+    
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') || 
+        Object.isFunction(element)) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;    
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = Object.isString(effect.options.queue) ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+    
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+    
+    eval('this.render = function(pos){ '+
+      'if (this.state=="idle"){this.state="running";'+
+      codeForEvent(this.options,'beforeSetup')+
+      (this.setup ? 'this.setup();':'')+ 
+      codeForEvent(this.options,'afterSetup')+
+      '};if (this.state=="running"){'+
+      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+      'this.position=pos;'+
+      codeForEvent(this.options,'beforeUpdate')+
+      (this.update ? 'this.update(pos);':'')+
+      codeForEvent(this.options,'afterUpdate')+
+      '}}');
+    
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(), 
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+    scrollOffsets = document.viewport.getScrollOffsets(),
+    elementOffsets = $(element).cumulativeOffset(),
+    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1] > max ? max : elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) { 
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity}); 
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from).show(); 
+  }}, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        Position.absolutize(effect.effects[0].element)
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      } 
+    }, arguments[1] || { })
+  );
+};
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        } 
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },  
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, arguments[1] || { })
+  );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { };
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+    
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        }
+      }
+    }
+    this.start(options);
+  },
+  
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return { 
+        style: property.camelize(), 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      };
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] = 
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     track.keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]); 
+  });
+  
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
+      hash.set(property, css[property]);
+      return hash;
+    });
+    if (!styles.opacity) styles.set('opacity', element.getOpacity());
+    return styles;
+  };
+};
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element)
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) { 
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    }
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/prototype.js b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/javascripts/prototype.js
new file mode 100644 (file)
index 0000000..546f9fe
--- /dev/null
@@ -0,0 +1,4225 @@
+/*  Prototype JavaScript framework, version 1.6.0.1
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0.1',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div').__proto__ &&
+      document.createElement('div').__proto__ !==
+        document.createElement('form').__proto__
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value, value = Object.extend((function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method), {
+          valueOf:  function() { return method },
+          toString: function() { return method.toString() }
+        });
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (Object.isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (!Object.isUndefined(value))
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({ }, object);
+  },
+
+  isElement: function(object) {
+    return object && object.nodeType == 1;
+  },
+
+  isArray: function(object) {
+    return object && object.constructor === Array;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    }.bind(this));
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    iterator = iterator.bind(context);
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator, context) {
+    iterator = iterator.bind(context);
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    iterator = iterator.bind(context);
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  function $A(iterable) {
+    if (!iterable) return [];
+    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+        iterable.toArray) return iterable.toArray();
+    var length = iterable.length, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  }
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (!Object.isUndefined(value)) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.map(function(pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return values.map(toQueryPair.curry(key)).join('&');
+        }
+        return toQueryPair(key, values);
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON);
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
+    try {
+      return this.responseText.evalJSON(options.sanitizeJSON);
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = Object.clone(options);
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, json) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, t, range;
+
+    for (position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      t = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        t.insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      range = element.ownerDocument.createRange();
+      t.initializeRange(element, range);
+      t.insert(element, range.createContextualFragment(content.stripScripts()));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $(element).getElementsBySelector("*");
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
+
+    for (var attr in attributes) {
+      name = t.names[attr] || attr;
+      value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    var originalAncestor = ancestor;
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (element.sourceIndex && !Prototype.Browser.Opera) {
+      var e = element.sourceIndex, a = ancestor.sourceIndex,
+       nextAncestor = ancestor.nextSibling;
+      if (!nextAncestor) {
+        do { ancestor = ancestor.parentNode; }
+        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+      }
+      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+    }
+
+    while (element = element.parentNode)
+      if (element == originalAncestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    if (element.offsetParent) return $(element.offsetParent);
+    if (element == document.body) return $(element);
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(document.body);
+  },
+
+  viewportOffset: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+
+if (!document.createRange || Prototype.Browser.Opera) {
+  Element.Methods.insert = function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = { bottom: insertions };
+
+    var t = Element._insertionTranslations, content, position, pos, tagName;
+
+    for (position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      pos      = t[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        pos.insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      if (t.tags[tagName]) {
+        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+        if (position == 'top' || position == 'after') fragments.reverse();
+        fragments.each(pos.insert.curry(element));
+      }
+      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  };
+}
+
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
+      }
+    }
+  );
+
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
+    }
+  );
+}
+
+else if (Prototype.Browser.IE) {
+  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        var position = element.getStyle('position');
+        if (position != 'static') return proceed(element);
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.clone(Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if (document.createElement('div').outerHTML) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  div.innerHTML = t[0] + html + t[1];
+  t[2].times(function() { div = div.firstChild });
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: {
+    adjacency: 'beforeBegin',
+    insert: function(element, node) {
+      element.parentNode.insertBefore(node, element);
+    },
+    initializeRange: function(element, range) {
+      range.setStartBefore(element);
+    }
+  },
+  top: {
+    adjacency: 'afterBegin',
+    insert: function(element, node) {
+      element.insertBefore(node, element.firstChild);
+    },
+    initializeRange: function(element, range) {
+      range.selectNodeContents(element);
+      range.collapse(true);
+    }
+  },
+  bottom: {
+    adjacency: 'beforeEnd',
+    insert: function(element, node) {
+      element.appendChild(node);
+    }
+  },
+  after: {
+    adjacency: 'afterEnd',
+    insert: function(element, node) {
+      element.parentNode.insertBefore(node, element.nextSibling);
+    },
+    initializeRange: function(element, range) {
+      range.setStartAfter(element);
+    }
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  this.bottom.initializeRange = this.top.initializeRange;
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div').__proto__) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName, property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { };
+    var B = Prototype.Browser;
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+      return false;
+
+    return true;
+  },
+
+  compileMatcher: function() {
+    if (this.shouldUseXPath())
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+             new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
+    attr: function(m) {
+      m[1] = m[1].toLowerCase();
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return Selector.operators[matches[2]](nodeValue, matches[3]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+             if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(',');
+    expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+if (Prototype.Browser.IE) {
+  // IE returns comment nodes on getElementsByTagName("*").
+  // Filter them out.
+  Selector.handlers.concat = function(a, b) {
+    for (var i = 0, node; node = b[i]; i++)
+      if (node.tagName !== "!") a.push(node);
+    return a;
+  };
+}
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (Object.isUndefined(options.hash)) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, index) {
+    if (Object.isUndefined(index))
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, value, single = !Object.isArray(index);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        value = this.optionValue(opt);
+        if (single) {
+          if (value == index) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = index.include(value);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      var node = Event.extend(event).target;
+      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
+    },
+
+    pointer: function(event) {
+      return {
+        x: event.pageX || (event.clientX +
+          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+        y: event.pageY || (event.clientY +
+          (document.documentElement.scrollTop || document.body.scrollTop))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._eventID) return element._eventID;
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._eventID = ++arguments.callee.id;
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event)
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      if (document.createEvent) {
+        var event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        var event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return Event.extend(event);
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize()
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer, fired = false;
+
+  function fireContentLoadedEvent() {
+    if (fired) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    fired = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/robots.txt b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/public/robots.txt
new file mode 100644 (file)
index 0000000..085187f
--- /dev/null
@@ -0,0 +1,5 @@
+# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
+#
+# To ban all spiders from the entire site uncomment the next two lines:
+# User-Agent: *
+# Disallow: /
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/about b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/about
new file mode 100644 (file)
index 0000000..cd38a32
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/about'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/console b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/console
new file mode 100644 (file)
index 0000000..498077a
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/console'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/destroy b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/destroy
new file mode 100644 (file)
index 0000000..a4df765
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/destroy'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/generate b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/generate
new file mode 100644 (file)
index 0000000..173a9f1
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/generate'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/benchmarker b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/benchmarker
new file mode 100644 (file)
index 0000000..c842d35
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/benchmarker'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/profiler b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/profiler
new file mode 100644 (file)
index 0000000..d855ac8
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/profiler'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/request b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/performance/request
new file mode 100644 (file)
index 0000000..ae3f38c
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/request'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/plugin b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/plugin
new file mode 100644 (file)
index 0000000..87cd207
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/plugin'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/inspector b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/inspector
new file mode 100644 (file)
index 0000000..bf25ad8
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/inspector'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/reaper b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/reaper
new file mode 100644 (file)
index 0000000..c77f045
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/reaper'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/spawner b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/process/spawner
new file mode 100644 (file)
index 0000000..7118f39
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spawner'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/runner b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/runner
new file mode 100644 (file)
index 0000000..a4a7cb2
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/runner'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/server b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/rails_app/script/server
new file mode 100644 (file)
index 0000000..3c67f39
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/server'
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/mongrel_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/mongrel_spec.rb
new file mode 100644 (file)
index 0000000..20cc335
--- /dev/null
@@ -0,0 +1,39 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+require 'digest/sha1'
+
+describe Request, 'legacy Mongrel tests' do
+  it 'should raise error on large header names' do
+    proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(1024))}: Test\r\n\r\n") }.
+      should raise_error(InvalidRequest)
+  end
+
+  it 'should raise error on large mangled field values' do
+    proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024*1024, false)}\r\n\r\n") }.
+      should raise_error(InvalidRequest)
+  end
+  
+  it 'should raise error on big fat ugly headers' do
+    get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
+    get << "X-Test: test\r\n" * (80 * 1024)
+    proc { R(get) }.should raise_error(InvalidRequest)
+  end
+
+  it 'should raise error on random garbage' do
+    proc { R("GET #{rand_data(1024, 1024+(1024), false)} #{rand_data(1024, 1024+(1024), false)}\r\n\r\n") }.
+      should raise_error(InvalidRequest)
+  end
+  
+  private
+    def rand_data(min, max, readable=true)
+      count = min + ((rand(max)+1) *10).to_i
+      res = count.to_s + "/"
+
+      if readable
+        res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
+      else
+        res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
+      end
+
+      return res
+    end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/parser_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/parser_spec.rb
new file mode 100644 (file)
index 0000000..e221162
--- /dev/null
@@ -0,0 +1,254 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+# Require mongrel so we can test that Thin parser don't clash w/ Mongrel parser.
+begin
+  require 'mongrel'
+rescue LoadError
+  warn "Install mongrel to test compatibility w/ it"
+end
+
+describe Request, 'parser' do
+  it 'should include basic headers' do
+    request = R("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
+    request.env['SERVER_PROTOCOL'].should == 'HTTP/1.1'
+    request.env['REQUEST_PATH'].should == '/'
+    request.env['HTTP_VERSION'].should == 'HTTP/1.1'
+    request.env['REQUEST_URI'].should == '/'
+    request.env['GATEWAY_INTERFACE'].should == 'CGI/1.2'
+    request.env['REQUEST_METHOD'].should == 'GET'
+    request.env["rack.url_scheme"].should == 'http'
+    request.env['FRAGMENT'].to_s.should be_empty
+    request.env['QUERY_STRING'].to_s.should be_empty
+    
+    request.should validate_with_lint
+  end
+  
+  it 'should not prepend HTTP_ to Content-Type and Content-Length' do
+    request = R("POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\naa")
+    request.env.keys.should_not include('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH')
+    request.env.keys.should include('CONTENT_TYPE', 'CONTENT_LENGTH')
+    
+    request.should validate_with_lint
+  end
+  
+  it 'should raise error on invalid request line' do
+    proc { R("GET / SsUTF/1.1") }.should raise_error(InvalidRequest)
+    proc { R("GET / HTTP/1.1yousmelllikecheeze") }.should raise_error(InvalidRequest)
+  end
+  
+  it 'should support fragment in uri' do
+    request = R("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\nHost: localhost\r\n\r\n")
+
+    request.env['REQUEST_URI'].should == '/forums/1/topics/2375?page=1'
+    request.env['PATH_INFO'].should == '/forums/1/topics/2375'
+    request.env['QUERY_STRING'].should == 'page=1'
+    request.env['FRAGMENT'].should == 'posts-17408'
+    
+    request.should validate_with_lint
+  end
+  
+  it 'should parse path with query string' do
+    request = R("GET /index.html?234235 HTTP/1.1\r\nHost: localhost\r\n\r\n")
+    request.env['REQUEST_PATH'].should == '/index.html'
+    request.env['QUERY_STRING'].should == '234235'
+    request.env['FRAGMENT'].should be_nil
+    
+    request.should validate_with_lint
+  end
+  
+  it 'should parse headers from GET request' do
+    request = R(<<-EOS, true)
+GET / HTTP/1.1
+Host: myhost.com:3000
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
+Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Cookie: mium=7
+Keep-Alive: 300
+Connection: keep-alive
+
+EOS
+    request.env['HTTP_HOST'].should == 'myhost.com:3000'
+    request.env['SERVER_NAME'].should == 'myhost.com'
+    request.env['SERVER_PORT'].should == '3000'
+    request.env['HTTP_COOKIE'].should == 'mium=7'
+
+    request.should validate_with_lint
+  end
+
+  it 'should parse POST request with data' do
+    request = R(<<-EOS.chomp, true)
+POST /postit HTTP/1.1
+Host: localhost:3000
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
+Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Keep-Alive: 300
+Connection: keep-alive
+Content-Type: text/html
+Content-Length: 37
+
+name=marc&email=macournoyer@gmail.com
+EOS
+
+    request.env['REQUEST_METHOD'].should == 'POST'
+    request.env['REQUEST_URI'].should == '/postit'
+    request.env['CONTENT_TYPE'].should == 'text/html'
+    request.env['CONTENT_LENGTH'].should == '37'
+    request.env['HTTP_ACCEPT'].should == 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'
+    request.env['HTTP_ACCEPT_LANGUAGE'].should == 'en-us,en;q=0.5'
+
+    request.body.rewind
+    request.body.read.should == 'name=marc&email=macournoyer@gmail.com'
+    request.body.class.should == StringIO
+
+    request.should validate_with_lint
+  end
+
+  it 'should not fuck up on stupid fucked IE6 headers' do
+    body = <<-EOS
+POST /codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype/refactors HTTP/1.0
+X-Real-IP: 62.24.71.95
+X-Forwarded-For: 62.24.71.95
+Host: refactormycode.com
+Connection: close
+TE: deflate,gzip;q=0.3
+Accept: */*
+Range: bytes=0-499999
+Referer: http://refactormycode.com/codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype
+User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
+Content-Length: 1
+Content-Type: application/x-www-form-urlencoded
+Cookie: _refactormycode_session_id=a1b2n3jk4k5; flash=%7B%7D
+Cookie2: $Version="1"
+
+a
+EOS
+    request = R(body, true)
+    request.env['HTTP_COOKIE2'].should == '$Version="1"'
+
+    request.should validate_with_lint
+  end
+
+  it 'shoud accept long query string' do
+    body = <<-EOS
+GET /session?open_id_complete=1&nonce=ytPOcwni&nonce=ytPOcwni&openid.assoc_handle=%7BHMAC-SHA1%7D%7B473e38fe%7D%7BJTjJxA%3D%3D%7D&openid.identity=http%3A%2F%2Fmacournoyer.myopenid.com%2F&openid.mode=id_res&openid.op_endpoint=http%3A%2F%2Fwww.myopenid.com%2Fserver&openid.response_nonce=2007-11-29T01%3A19%3A35ZGA5FUU&openid.return_to=http%3A%2F%2Flocalhost%3A3000%2Fsession%3Fopen_id_complete%3D1%26nonce%3DytPOcwni%26nonce%3DytPOcwni&openid.sig=lPIRgwpfR6JAdGGnb0ZjcY%2FWjr8%3D&openid.signed=assoc_handle%2Cidentity%2Cmode%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned%2Csreg.email%2Csreg.nickname&openid.sreg.email=macournoyer%40yahoo.ca&openid.sreg.nickname=macournoyer HTTP/1.1
+Host: localhost:3000
+
+EOS
+    request = R(body, true)
+
+    request.env['QUERY_STRING'].should == 'open_id_complete=1&nonce=ytPOcwni&nonce=ytPOcwni&openid.assoc_handle=%7BHMAC-SHA1%7D%7B473e38fe%7D%7BJTjJxA%3D%3D%7D&openid.identity=http%3A%2F%2Fmacournoyer.myopenid.com%2F&openid.mode=id_res&openid.op_endpoint=http%3A%2F%2Fwww.myopenid.com%2Fserver&openid.response_nonce=2007-11-29T01%3A19%3A35ZGA5FUU&openid.return_to=http%3A%2F%2Flocalhost%3A3000%2Fsession%3Fopen_id_complete%3D1%26nonce%3DytPOcwni%26nonce%3DytPOcwni&openid.sig=lPIRgwpfR6JAdGGnb0ZjcY%2FWjr8%3D&openid.signed=assoc_handle%2Cidentity%2Cmode%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned%2Csreg.email%2Csreg.nickname&openid.sreg.email=macournoyer%40yahoo.ca&openid.sreg.nickname=macournoyer'
+
+    request.should validate_with_lint
+  end
+  
+  it 'should parse even with stupid Content-Length' do
+    body = <<-EOS.chomp
+POST / HTTP/1.1
+Host: localhost:3000
+Content-Length: 300
+
+aye
+EOS
+    request = R(body, true)
+
+    request.body.rewind
+    request.body.read.should == 'aye'
+  end
+  
+  it "should parse ie6 urls" do
+    %w(/some/random/path"
+       /some/random/path>
+       /some/random/path<
+       /we/love/you/ie6?q=<"">
+       /url?<="&>="
+       /mal"formed"?
+    ).each do |path|
+      parser     = HttpParser.new
+      req        = {}
+      sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
+      nread      = parser.execute(req, sorta_safe, 0)
+
+      sorta_safe.size.should == nread - 1 # Ragel 6 skips last linebreak
+      parser.should be_finished
+      parser.should_not be_error
+    end
+  end
+  
+  xit "should parse absolute request URI" do
+    request = R(<<-EOS, true)
+GET http://localhost:3000/hi HTTP/1.1
+Host: localhost:3000
+
+EOS
+    request.env['PATH_INFO'].should == '/hi'
+
+    request.should validate_with_lint
+  end
+  
+  
+  it "should fails on heders larger then MAX_HEADER" do
+    proc { R("GET / HTTP/1.1\r\nFoo: #{'X' * Request::MAX_HEADER}\r\n\r\n") }.should raise_error(InvalidRequest)
+  end
+  
+  it "should default SERVER_NAME to localhost" do
+    request = R("GET / HTTP/1.1\r\n\r\n")
+    request.env['SERVER_NAME'].should == "localhost"
+  end
+  
+  it 'should normalize http_fields' do
+    [ "GET /index.html HTTP/1.1\r\nhos-t: localhost\r\n\r\n",
+      "GET /index.html HTTP/1.1\r\nhOs_t: localhost\r\n\r\n",
+      "GET /index.html HTTP/1.1\r\nhoS-T: localhost\r\n\r\n"
+    ].each { |req_str|
+      parser     = HttpParser.new
+      req        = {}
+      nread      = parser.execute(req, req_str, 0)
+      req.should have_key('HTTP_HOS_T')
+    }
+  end
+  
+  it "should parse PATH_INFO with semicolon" do
+    qs = "QUERY_STRING"
+    pi = "PATH_INFO"
+    {
+      "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
+      "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
+      "/1;a=b" => { qs => "", pi => "/1;a=b" },
+      "/1;a=b?" => { qs => "", pi => "/1;a=b" },
+      "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
+      "*" => { qs => "", pi => "" },
+    }.each do |uri, expect|
+      parser     = HttpParser.new
+      env        = {}
+      nread      = parser.execute(env, "GET #{uri} HTTP/1.1\r\nHost: www.example.com\r\n\r\n", 0)
+
+      env[pi].should == expect[pi]
+      env[qs].should == expect[qs]
+      env["REQUEST_URI"].should == uri
+
+      next if uri == "*"
+      
+      # Validate w/ Ruby's URI.parse
+      uri = URI.parse("http://example.com#{uri}")
+      env[qs].should == uri.query.to_s
+      env[pi].should == uri.path
+    end
+  end
+  
+  it "should parse IE7 badly encoded URL" do
+    body = <<-EOS.chomp
+GET /H%uFFFDhnchenbrustfilet HTTP/1.1
+Host: localhost:3000
+
+EOS
+    request = R(body, true)
+
+    request.env['REQUEST_URI'].should == "/H%uFFFDhnchenbrustfilet"
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/persistent_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/persistent_spec.rb
new file mode 100644 (file)
index 0000000..c6d05a6
--- /dev/null
@@ -0,0 +1,35 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Request, 'persistent' do
+  before do
+    @request = Request.new
+  end
+  
+  it "should not assume that a persistent connection is maintained for HTTP version 1.0" do
+    @request.env['HTTP_VERSION'] = 'HTTP/1.0'
+    @request.should_not be_persistent
+  end
+
+  it "should assume that a persistent connection is maintained for HTTP version 1.0 when specified" do
+    @request.env['HTTP_VERSION'] = 'HTTP/1.0'
+    @request.env['HTTP_CONNECTION'] = 'Keep-Alive'
+    @request.should be_persistent
+  end
+  
+  it "should maintain a persistent connection for HTTP/1.1 client" do
+    @request.env['HTTP_VERSION'] = 'HTTP/1.1'
+    @request.env['HTTP_CONNECTION'] = 'Keep-Alive'
+    @request.should be_persistent
+  end
+
+  it "should maintain a persistent connection for HTTP/1.1 client by default" do
+    @request.env['HTTP_VERSION'] = 'HTTP/1.1'
+    @request.should be_persistent
+  end
+
+  it "should not maintain a persistent connection for HTTP/1.1 client when Connection header include close" do
+    @request.env['HTTP_VERSION'] = 'HTTP/1.1'
+    @request.env['HTTP_CONNECTION'] = 'close'
+    @request.should_not be_persistent
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/processing_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/request/processing_spec.rb
new file mode 100644 (file)
index 0000000..c2deffb
--- /dev/null
@@ -0,0 +1,50 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Request, 'processing' do
+  it 'should parse in chunks' do
+    request = Request.new
+    request.parse("POST / HTTP/1.1\r\n").should be_false
+    request.parse("Host: localhost\r\n").should be_false
+    request.parse("Content-Length: 9\r\n").should be_false
+    request.parse("\r\nvery ").should be_false
+    request.parse("cool").should be_true
+
+    request.env['CONTENT_LENGTH'].should == '9'
+    request.body.read.should == 'very cool'
+    request.should validate_with_lint
+  end
+
+  it "should move body to tempfile when too big" do
+    len = Request::MAX_BODY + 2
+    request = Request.new
+    request.parse("POST /postit HTTP/1.1\r\nContent-Length: #{len}\r\n\r\n#{'X' * (len/2)}")
+    request.parse('X' * (len/2))
+
+    request.body.size.should == len
+    request.should be_finished
+    request.body.class.should == Tempfile
+  end
+
+  it "should delete body tempfile when closing" do
+    body = 'X' * (Request::MAX_BODY + 1)
+    
+    request = Request.new
+    request.parse("POST /postit HTTP/1.1\r\n")
+    request.parse("Content-Length: #{body.size}\r\n\r\n")
+    request.parse(body)
+
+    request.body.path.should_not be_nil
+    request.close
+    request.body.path.should be_nil
+  end
+
+  it "should raise error when header is too big" do
+    big_headers = "X-Test: X\r\n" * (1024 * (80 + 32))
+    proc { R("GET / HTTP/1.1\r\n#{big_headers}\r\n") }.should raise_error(InvalidRequest)
+  end
+  
+  it "should set body external encoding to ASCII_8BIT" do
+    pending("Ruby 1.9 compatible implementations only") unless StringIO.instance_methods.include? :external_encoding
+    Request.new.body.external_encoding.should == Encoding::ASCII_8BIT
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/response_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/response_spec.rb
new file mode 100644 (file)
index 0000000..f241742
--- /dev/null
@@ -0,0 +1,102 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Response do
+  before do
+    @response = Response.new
+    @response.headers['Content-Type'] = 'text/html'
+    @response.headers['Content-Length'] = '0'
+    @response.body = ''
+  end
+  
+  it 'should output headers' do
+    @response.headers_output.should include("Content-Type: text/html", "Content-Length: 0", "Connection: close")
+  end
+  
+  it 'should include server name header' do
+    @response.headers_output.should include("Server: thin")
+  end
+  
+  it 'should output head' do
+    @response.head.should include("HTTP/1.1 200 OK", "Content-Type: text/html", "Content-Length: 0",
+                                  "Connection: close", "\r\n\r\n")
+  end
+  
+  it 'should allow duplicates in headers' do
+    @response.headers['Set-Cookie'] = 'mium=7'
+    @response.headers['Set-Cookie'] = 'hi=there'
+    
+    @response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
+  end
+  
+  it 'should parse simple header values' do
+    @response.headers = {
+      'Host' => 'localhost'
+    }
+    
+    @response.head.should include("Host: localhost")
+  end
+  
+  it 'should parse multiline header values in several headers' do
+    @response.headers = {
+      'Set-Cookie' => "mium=7\nhi=there"
+    }
+    
+    @response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
+  end
+
+  it 'should ignore nil headers' do
+    @response.headers = nil
+    @response.headers = { 'Host' => 'localhost' }
+    @response.headers = { 'Set-Cookie' => nil }
+    @response.head.should include('Host: localhost')
+  end
+  
+  it 'should output body' do
+    @response.body = ['<html>', '</html>']
+    
+    out = ''
+    @response.each { |l| out << l }
+    out.should include("\r\n\r\n<html></html>")
+  end
+    
+  it 'should output String body' do
+    @response.body = '<html></html>'
+    
+    out = ''
+    @response.each { |l| out << l }
+    out.should include("\r\n\r\n<html></html>")
+  end
+    
+  it "should not be persistent by default" do
+    @response.should_not be_persistent
+  end
+  
+  it "should not be persistent when no Content-Length" do
+    @response = Response.new
+    @response.headers['Content-Type'] = 'text/html'
+    @response.body = ''
+    
+    @response.persistent!
+    @response.should_not be_persistent
+  end
+  
+  it "should be persistent when the status code implies it should stay open" do
+    @response = Response.new
+    @response.status = 100
+    # "There are no required headers for this class of status code" -- HTTP spec 10.1
+    @response.body = ''
+
+    # Specifying it as persistent in the code is NOT required
+    # @response.persistent!
+    @response.should be_persistent
+  end
+  
+  it "should be persistent when specified" do
+    @response.persistent!
+    @response.should be_persistent
+  end
+  
+  it "should be closeable" do
+    @response.close
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/runner_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/runner_spec.rb
new file mode 100644 (file)
index 0000000..5732891
--- /dev/null
@@ -0,0 +1,168 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Runner do
+  it "should parse options" do
+    runner = Runner.new(%w(start --pid test.pid --port 5000 -o 3000))
+    
+    runner.options[:pid].should == 'test.pid'
+    runner.options[:port].should == 5000
+    runner.options[:only].should == 3000
+  end
+  
+  it "should parse specified command" do
+    Runner.new(%w(start)).command.should == 'start'
+    Runner.new(%w(stop)).command.should == 'stop'
+    Runner.new(%w(restart)).command.should == 'restart'
+  end
+  
+  it "should abort on unknow command" do
+    runner = Runner.new(%w(poop))
+    
+    runner.should_receive(:abort)
+    runner.run!
+  end
+  
+  it "should exit on empty command" do
+    runner = Runner.new([])
+    
+    runner.should_receive(:exit).with(1)
+    
+    silence_stream(STDOUT) do
+      runner.run!
+    end
+  end
+  
+  it "should use Controller when controlling a single server" do
+    runner = Runner.new(%w(start))
+    
+    controller = mock('controller')
+    controller.should_receive(:start)
+    Controllers::Controller.should_receive(:new).and_return(controller)
+    
+    runner.run!
+  end
+
+  it "should use Cluster controller when controlling multiple servers" do
+    runner = Runner.new(%w(start --servers 3))
+    
+    controller = mock('cluster')
+    controller.should_receive(:start)
+    Controllers::Cluster.should_receive(:new).and_return(controller)
+    
+    runner.run!
+  end
+  
+  it "should default to single server controller" do
+    Runner.new(%w(start)).should_not be_a_cluster
+  end
+  
+  it "should consider as a cluster with :servers option" do
+    Runner.new(%w(start --servers 3)).should be_a_cluster
+  end
+  
+  it "should consider as a cluster with :only option" do
+    Runner.new(%w(start --only 3000)).should be_a_cluster
+  end
+  
+  it "should warn when require a rack config file" do
+    STDERR.stub!(:write)
+    STDERR.should_receive(:write).with(/WARNING:/)
+    
+    runner = Runner.new(%w(start -r config.ru))
+    runner.run! rescue nil
+    
+    runner.options[:rackup].should == 'config.ru'
+  end
+  
+  it "should require file" do
+    runner = Runner.new(%w(start -r unexisting))
+    proc { runner.run! }.should raise_error(LoadError)
+  end
+  
+  it "should remember requires" do
+    runner = Runner.new(%w(start -r rubygems -r thin))
+    runner.options[:require].should == %w(rubygems thin)
+  end
+
+  it "should remember debug options" do
+    runner = Runner.new(%w(start -D -V))
+    runner.options[:debug].should be_true
+    runner.options[:trace].should be_true
+  end
+
+  it "should default debug and trace to false" do
+    runner = Runner.new(%w(start))
+    runner.options[:debug].should_not be_true
+    runner.options[:trace].should_not be_true
+  end
+end
+
+describe Runner, 'with config file' do
+  before do
+    @runner = Runner.new(%w(start --config spec/configs/cluster.yml))
+  end
+  
+  it "should load options from file with :config option" do
+    @runner.send :load_options_from_config_file!
+    
+    @runner.options[:environment].should == 'production'
+    @runner.options[:chdir].should == 'spec/rails_app'
+    @runner.options[:port].should == 5000
+    @runner.options[:servers].should == 3
+  end
+  
+  it "should change directory after loading config" do
+    @orig_dir = Dir.pwd
+    
+    controller = mock('controller')
+    controller.should_receive(:respond_to?).with('start').and_return(true)
+    controller.should_receive(:start)
+    Controllers::Cluster.should_receive(:new).and_return(controller)
+    expected_dir = File.expand_path('spec/rails_app')
+    
+    begin
+      silence_stream(STDERR) do
+        @runner.run!
+      end
+  
+      Dir.pwd.should == expected_dir
+    
+    ensure
+      # any other spec using relative paths should work as expected
+      Dir.chdir(@orig_dir)
+    end
+  end
+end
+
+describe Runner, "service" do
+  before do
+    Thin.stub!(:linux?).and_return(true)
+    
+    @controller = mock('service')
+    Controllers::Service.stub!(:new).and_return(@controller)
+  end
+  
+  it "should use Service controller when controlling all servers" do
+    runner = Runner.new(%w(start --all))
+    
+    @controller.should_receive(:start)
+    
+    runner.run!
+  end
+  
+  it "should call install with arguments" do
+    runner = Runner.new(%w(install /etc/cool))
+    
+    @controller.should_receive(:install).with('/etc/cool')
+    
+    runner.run!
+  end
+
+  it "should call install without arguments" do
+    runner = Runner.new(%w(install))
+    
+    @controller.should_receive(:install).with()
+    
+    runner.run!
+  end  
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/builder_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/builder_spec.rb
new file mode 100644 (file)
index 0000000..9164572
--- /dev/null
@@ -0,0 +1,44 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, 'app builder' do
+  it "should build app from constructor" do
+    app = proc {}
+    server = Server.new('0.0.0.0', 3000, app)
+    
+    server.app.should == app
+  end
+  
+  it "should build app from builder block" do
+    server = Server.new '0.0.0.0', 3000 do
+      run(proc { |env| :works })
+    end
+    
+    server.app.call({}).should == :works
+  end
+  
+  it "should use middlewares in builder block" do
+    server = Server.new '0.0.0.0', 3000 do
+      use Rack::ShowExceptions
+      run(proc { |env| :works })
+    end
+    
+    server.app.class.should == Rack::ShowExceptions
+    server.app.call({}).should == :works
+  end
+  
+  it "should work with Rack url mapper" do
+    server = Server.new '0.0.0.0', 3000 do
+      map '/test' do
+        run(proc { |env| [200, {}, 'Found /test'] })
+      end
+    end
+    
+    default_env = { 'SCRIPT_NAME' => '' }
+    
+    server.app.call(default_env.update('PATH_INFO' => '/'))[0].should == 404
+    
+    status, headers, body = server.app.call(default_env.update('PATH_INFO' => '/test'))
+    status.should == 200
+    body.should == 'Found /test'
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/pipelining_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/pipelining_spec.rb
new file mode 100644 (file)
index 0000000..7c02aba
--- /dev/null
@@ -0,0 +1,110 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, "HTTP pipelining" do
+  before do
+    calls = 0
+    start_server do |env|
+      calls += 1
+      body = env['PATH_INFO'] + '-' + calls.to_s
+      [200, { 'Content-Type' => 'text/html' }, body]
+    end
+    @server.maximum_persistent_connections = 1024
+  end
+  
+  it "should pipeline request on same socket" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write "GET /first HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
+    socket.flush
+    socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
+    socket.flush
+    response = socket.read
+    socket.close
+    
+    wait_for_requests_to_complete!
+    
+    response.should include('/first-1', '/second-2')
+  end
+  
+  it "should pipeline requests by default on HTTP 1.1" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write "GET /first HTTP/1.1\r\n\r\n"
+    socket.flush
+    socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
+    socket.flush
+    response = socket.read
+    socket.close
+    
+    wait_for_requests_to_complete!
+    
+    response.should include('/first-1', '/second-2')
+  end
+  
+  it "should not pipeline request by default on HTTP 1.0" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write "GET /first HTTP/1.0\r\n\r\n"
+    socket.flush
+    socket.write "GET /second HTTP/1.0\r\nConnection: close\r\n\r\n"
+    response = socket.read
+    socket.close
+    
+    wait_for_requests_to_complete!
+    
+    response.should include('/first-1')
+    response.should_not include('/second-2')
+  end
+  
+  it "should not pipeline request on same socket when connection is closed" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write "GET /first HTTP/1.1\r\nConnection: close\r\n\r\n"
+    socket.flush
+    socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
+    response = socket.read
+    socket.close
+    
+    wait_for_requests_to_complete!
+    
+    response.should include('/first-1')
+    response.should_not include('/second-2')
+  end
+  
+  it "should not allow more persistent connection then maximum" do
+    @server.maximum_persistent_connections = 1
+    
+    socket1 = TCPSocket.new('0.0.0.0', 3333)
+    socket1.write "GET / HTTP/1.1\r\n\r\n"
+    socket1.flush
+    socket2 = TCPSocket.new('0.0.0.0', 3333)    
+    socket2.write "GET / HTTP/1.1\r\n\r\n"
+    socket2.flush
+    
+    @server.backend.persistent_connection_count.should == 1
+    @server.backend.size.should == 2
+    
+    socket1.close    
+    socket2.close
+  end
+  
+  it "should decrement persistent connection on close" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write "GET / HTTP/1.1\r\n\r\n"
+    socket.flush
+    
+    @server.backend.persistent_connection_count.should == 1
+    
+    socket.write "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
+    socket.close
+    
+    wait_for_requests_to_complete!
+    
+    @server.backend.persistent_connection_count.should == 0
+  end
+  
+  after do
+    stop_server
+  end
+  
+  private
+    def wait_for_requests_to_complete!
+      sleep 0.1 until @server.backend.size == 0
+    end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/robustness_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/robustness_spec.rb
new file mode 100644 (file)
index 0000000..8e99bb3
--- /dev/null
@@ -0,0 +1,34 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, 'robustness' do
+  before do
+    start_server do |env|
+      body = 'hello!'
+      [200, { 'Content-Type' => 'text/html' }, body]
+    end
+  end
+  
+  it "should not crash when header too large" do
+    100.times do
+      begin
+        socket = TCPSocket.new(DEFAULT_TEST_ADDRESS, DEFAULT_TEST_PORT)
+        socket.write("GET / HTTP/1.1\r\n")
+        socket.write("Host: localhost\r\n")
+        socket.write("Connection: close\r\n")
+        10000.times do
+               socket.write("X-Foo: #{'x' * 100}\r\n")
+               socket.flush
+        end
+        socket.write("\r\n")
+        socket.read
+        socket.close
+      rescue Errno::EPIPE, Errno::ECONNRESET
+                               # Ignore.
+                       end
+    end
+  end
+  
+  after do
+    stop_server
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/stopping_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/stopping_spec.rb
new file mode 100644 (file)
index 0000000..ad12c64
--- /dev/null
@@ -0,0 +1,55 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, "stopping" do
+  before do
+    start_server do |env|
+      [200, { 'Content-Type' => 'text/html' }, ['ok']]
+    end
+    @done = false
+  end
+  
+  it "should wait for current requests before soft stopping" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write("GET / HTTP/1.1")
+    EventMachine.next_tick do
+      @server.stop # Stop the server in the middle of a request
+      socket.write("\r\n\r\n")
+      @done = true
+    end
+    
+    timeout(2) do
+      Thread.pass until @done
+    end
+    
+    out = socket.read
+    socket.close
+    
+    out.should_not be_empty
+  end
+  
+  it "should not accept new requests when soft stopping" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write("GET / HTTP/1.1")
+    @server.stop # Stop the server in the middle of a request
+    
+    EventMachine.next_tick do
+      proc { get('/') }.should raise_error(Errno::ECONNRESET)
+    end
+    
+    socket.close
+  end
+  
+  it "should drop current requests when hard stopping" do
+    socket = TCPSocket.new('0.0.0.0', 3333)
+    socket.write("GET / HTTP/1.1")
+    @server.stop! # Force stop the server in the middle of a request
+    
+    EventMachine.next_tick do
+      socket.should be_closed
+    end
+  end
+  
+  after do
+    stop_server
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply.yml b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply.yml
new file mode 100644 (file)
index 0000000..f339acd
--- /dev/null
@@ -0,0 +1,6 @@
+cluster_address: 0.0.0.0
+cluster_port: 3333
+map:
+  - incoming: 127.0.0.1
+    outgoing: 127.0.0.1:5555
+    default: true 
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/swiftiply_spec.rb
new file mode 100644 (file)
index 0000000..4272e5d
--- /dev/null
@@ -0,0 +1,32 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+if SWIFTIPLY_PATH.empty?
+  warn "Ignoring Server on Swiftiply specs, gem install swiftiply to run"
+else
+  describe Server, 'on Swiftiply' do
+    before do
+      @swiftiply = fork do
+        exec "#{SWIFTIPLY_PATH} -c #{File.dirname(__FILE__)}/swiftiply.yml"
+      end
+      wait_for_socket('0.0.0.0', 3333)
+      sleep 2 # HACK ooh boy, I wish I knew how to make those specs more stable...
+      start_server('0.0.0.0', 5555, :backend => Backends::SwiftiplyClient, :wait_for_socket => false) do |env|
+        body = env.inspect + env['rack.input'].read
+        [200, { 'Content-Type' => 'text/html' }, body]
+      end
+    end
+    
+    it 'should GET from Net::HTTP' do
+      Net::HTTP.get(URI.parse("http://0.0.0.0:3333/?cthis")).should include('cthis')
+    end
+  
+    it 'should POST from Net::HTTP' do
+      Net::HTTP.post_form(URI.parse("http://0.0.0.0:3333/"), :arg => 'pirate').body.should include('arg=pirate')
+    end
+  
+    after do
+      stop_server
+      Process.kill(9, @swiftiply)
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/tcp_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/tcp_spec.rb
new file mode 100644 (file)
index 0000000..841f39e
--- /dev/null
@@ -0,0 +1,57 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, 'on TCP socket' do
+  before do
+    start_server do |env|
+      body = env.inspect + env['rack.input'].read
+      [200, { 'Content-Type' => 'text/html' }, body]
+    end
+  end
+  
+  it 'should GET from Net::HTTP' do
+    get('/?cthis').should include('cthis')
+  end
+  
+  it 'should GET from TCPSocket' do
+    status, headers, body = parse_response(send_data("GET /?this HTTP/1.0\r\nConnection: close\r\n\r\n"))
+    status.should == 200
+    headers['Content-Type'].should == 'text/html'
+    headers['Connection'].should == 'close'
+    body.should include('this')
+  end
+  
+  it "should add the Content-Length to the response when not present" do
+    status, headers, body = parse_response(send_data("GET / HTTP/1.0\r\nConnection: close\r\n\r\n"))
+    headers.should have_key('Content-Length')
+  end
+  
+  it 'should set the Content-Length to equal the body size in bytes' do
+    status, headers, body = parse_response(send_data("GET / HTTP/1.0\r\nConnection: close\r\n\r\n"))
+    headers['Content-Length'].should == (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
+  end
+  
+  it 'should return empty string on incomplete headers' do
+    send_data("GET /?this HTTP/1.1\r\nHost:").should be_empty
+  end
+  
+  it 'should return empty string on incorrect Content-Length' do
+    send_data("POST / HTTP/1.1\r\nContent-Length: 300\r\nConnection: close\r\n\r\naye").should be_empty
+  end
+  
+  it 'should POST from Net::HTTP' do
+    post('/', :arg => 'pirate').should include('arg=pirate')
+  end
+  
+  it 'should handle big POST' do
+    big = 'X' * (20 * 1024)
+    post('/', :big => big).should include(big)
+  end
+  
+  it "should retreive remote address" do
+    get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
+  end
+  
+  after do
+    stop_server
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/threaded_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/threaded_spec.rb
new file mode 100644 (file)
index 0000000..27894c4
--- /dev/null
@@ -0,0 +1,27 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, 'with threads' do
+  before do
+    @requests = 0
+    start_server DEFAULT_TEST_ADDRESS, DEFAULT_TEST_PORT, :threaded => true do |env|
+      sleep env['PATH_INFO'].delete('/').to_i
+      @requests += 1
+      [200, { 'Content-Type' => 'text/html' }, 'hi']
+    end
+  end
+  
+  it "should process request" do
+    get('/').should_not be_empty
+  end
+  
+  it "should process requests when blocked" do
+    slow_request = Thread.new { get('/3') }
+    get('/').should_not be_empty
+    @requests.should == 1
+    slow_request.kill
+  end
+  
+  after do
+    stop_server
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/unix_socket_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server/unix_socket_spec.rb
new file mode 100644 (file)
index 0000000..326d48f
--- /dev/null
@@ -0,0 +1,26 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Server, "on UNIX domain socket" do
+  before do
+    start_server('/tmp/thin_test.sock') do |env|
+      [200, { 'Content-Type' => 'text/html' }, [env.inspect]]
+    end
+  end
+  
+  it "should accept GET request" do
+    get("/?this").should include('this')
+  end
+  
+  it "should retreive remote address" do    
+    get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
+  end
+  
+  it "should remove socket file after server stops" do
+    @server.stop!
+    File.exist?('/tmp/thin_test.sock').should be_false
+  end
+  
+  after do
+    stop_server
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server_spec.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/server_spec.rb
new file mode 100644 (file)
index 0000000..8e11b8b
--- /dev/null
@@ -0,0 +1,100 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Server do
+  before do
+    @server = Server.new('0.0.0.0', 3000)
+  end
+  
+  it "should set maximum_connections size" do
+    @server.maximum_connections = 100
+    @server.config
+    @server.maximum_connections.should == 100
+  end
+
+  it "should set lower maximum_connections size when too large" do
+    # root users under Linux will not have a limitation on maximum
+    # connections, so we cannot really run this test under that
+    # condition.
+    pending("only for non-root users") if Process.euid == 0
+    @server.maximum_connections = 100_000
+    @server.config
+    @server.maximum_connections.should < 100_000
+  end
+  
+  it "should default to non-threaded" do
+    @server.should_not be_threaded
+  end
+  
+  it "should set backend to threaded" do
+    @server.threaded = true
+    @server.backend.should be_threaded
+  end
+end
+
+describe Server, "initialization" do
+  it "should set host and port" do
+    server = Server.new('192.168.1.1', 8080)
+
+    server.host.should == '192.168.1.1'
+    server.port.should == 8080
+  end
+
+  it "should set socket" do
+    server = Server.new('/tmp/thin.sock')
+
+    server.socket.should == '/tmp/thin.sock'
+  end
+  
+  it "should set host, port and app" do
+    app = proc {}
+    server = Server.new('192.168.1.1', 8080, app)
+    
+    server.host.should_not be_nil
+    server.app.should == app
+  end
+
+  it "should set socket and app" do
+    app = proc {}
+    server = Server.new('/tmp/thin.sock', app)
+    
+    server.socket.should_not be_nil
+    server.app.should == app
+  end
+
+  it "should set socket, nil and app" do
+    app = proc {}
+    server = Server.new('/tmp/thin.sock', nil, app)
+    
+    server.socket.should_not be_nil
+    server.app.should == app
+  end
+  
+  it "should set host, port and backend" do
+    server = Server.new('192.168.1.1', 8080, :backend => Thin::Backends::SwiftiplyClient)
+    
+    server.host.should_not be_nil
+    server.backend.should be_kind_of(Thin::Backends::SwiftiplyClient)
+  end  
+
+  it "should set host, port, app and backend" do
+    app = proc {}
+    server = Server.new('192.168.1.1', 8080, app, :backend => Thin::Backends::SwiftiplyClient)
+    
+    server.host.should_not be_nil
+    server.app.should == app
+    server.backend.should be_kind_of(Thin::Backends::SwiftiplyClient)
+  end
+  
+  it "should set port as string" do
+    app = proc {}
+    server = Server.new('192.168.1.1', '8080')
+    
+    server.host.should == '192.168.1.1'
+    server.port.should == 8080
+  end
+  
+  it "should not register signals w/ :signals => false" do
+    Server.should_not_receive(:setup_signals)
+    Server.new(:signals => false)
+  end
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/spec_helper.rb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..04ab80f
--- /dev/null
@@ -0,0 +1,220 @@
+require 'rubygems'
+require 'thin'
+require 'spec'
+require 'benchmark'
+require 'timeout'
+require 'fileutils'
+require 'net/http'
+require 'socket'
+
+include Thin
+
+FileUtils.mkdir_p File.dirname(__FILE__) + '/../log'
+Command.script = File.dirname(__FILE__) + '/../bin/thin'
+Logging.silent = true
+
+unless Object.const_defined?(:SWIFTIPLY_PATH)
+  SWIFTIPLY_PATH       = `which swiftiply`.chomp
+  DEFAULT_TEST_ADDRESS = '0.0.0.0'
+  DEFAULT_TEST_PORT    = 3333
+end
+
+module Matchers
+  class BeFasterThen
+    def initialize(max_time)
+      require 'benchmark_unit'
+      @max_time = max_time
+    end
+
+    # Base on benchmark_unit/assertions#compare_benchmarks
+    def matches?(proc)
+      @time, multiplier = 0, 1
+      
+      while (@time < 0.01) do
+        @time = Benchmark::Unit.measure do 
+          multiplier.times &proc
+        end
+        multiplier *= 10
+      end
+      
+      multiplier /= 10
+      
+      iterations = (Benchmark::Unit::CLOCK_TARGET / @time).to_i * multiplier
+      iterations = 1 if iterations < 1
+      
+      total = Benchmark::Unit.measure do 
+        iterations.times &proc
+      end
+      
+      @time = total / iterations
+      
+      @time < @max_time
+    end
+    
+    def failure_message(less_more=:less)
+      "took <#{@time.inspect} RubySeconds>, should take #{less_more} than #{@max_time} RubySeconds."
+    end
+
+    def negative_failure_message
+      failure_message :more
+    end
+  end
+  
+  class ValidateWithLint
+    def matches?(request)
+      @request = request
+      Rack::Lint.new(proc{[200, {'Content-Type' => 'text/html', 'Content-Length' => '0'}, []]}).call(@request.env)
+      true
+    rescue Rack::Lint::LintError => e
+      @message = e.message
+      false
+    end
+    
+    def failure_message(negation=nil)
+      "should#{negation} validate with Rack Lint: #{@message}"
+    end
+
+    def negative_failure_message
+      failure_message ' not'
+    end
+  end
+
+  class TakeLessThen
+    def initialize(time)
+      @time = time
+    end
+    
+    def matches?(proc)
+      Timeout.timeout(@time) { proc.call }
+      true
+    rescue Timeout::Error
+      false 
+    end
+    
+    def failure_message(negation=nil)
+      "should#{negation} take less then #{@time} sec to run"
+    end
+
+    def negative_failure_message
+      failure_message ' not'
+    end
+  end
+
+  # Actual matchers that are exposed.
+
+  def be_faster_then(time)
+    BeFasterThen.new(time)
+  end
+  
+  def validate_with_lint
+    ValidateWithLint.new
+  end
+
+  def take_less_then(time)
+    TakeLessThen.new(time)
+  end  
+end
+
+module Helpers
+  # Silences any stream for the duration of the block.
+  #
+  #   silence_stream(STDOUT) do
+  #     puts 'This will never be seen'
+  #   end
+  #
+  #   puts 'But this will'
+  #
+  # (Taken from ActiveSupport)
+  def silence_stream(stream)
+    old_stream = stream.dup
+    stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
+    stream.sync = true
+    yield
+  ensure
+    stream.reopen(old_stream)
+  end
+  
+  # Create and parse a request
+  def R(raw, convert_line_feed=false)
+    raw.gsub!("\n", "\r\n") if convert_line_feed
+    request = Thin::Request.new
+    request.parse(raw)
+    request
+  end
+  
+  def start_server(address=DEFAULT_TEST_ADDRESS, port=DEFAULT_TEST_PORT, options={}, &app)
+    @server = Thin::Server.new(address, port, options, app)
+    @server.ssl = options[:ssl]
+    @server.threaded = options[:threaded]
+    @server.timeout = 3
+    
+    @thread = Thread.new { @server.start }
+    if options[:wait_for_socket]
+      wait_for_socket(address, port)
+    else
+      # If we can't ping the address fallback to just wait for the server to run
+      sleep 1 until @server.running?
+    end
+  end
+  
+  def stop_server
+    @server.stop!
+    @thread.kill
+    raise "Reactor still running, wtf?" if EventMachine.reactor_running?
+  end
+  
+  def wait_for_socket(address=DEFAULT_TEST_ADDRESS, port=DEFAULT_TEST_PORT, timeout=5)
+    Timeout.timeout(timeout) do
+      loop do
+        begin
+          if address.include?('/')
+            UNIXSocket.new(address).close
+          else
+            TCPSocket.new(address, port).close
+          end
+          return true
+        rescue
+        end
+      end
+    end
+  end
+    
+  def send_data(data)
+    if @server.backend.class == Backends::UnixServer
+      socket = UNIXSocket.new(@server.socket)
+    else
+      socket = TCPSocket.new(@server.host, @server.port)
+    end
+    socket.write data
+    out = socket.read
+    socket.close
+    out
+  end
+  
+  def parse_response(response)
+    raw_headers, body = response.split("\r\n\r\n", 2)
+    raw_status, raw_headers = raw_headers.split("\r\n", 2)
+
+    status  = raw_status.match(%r{\AHTTP/1.1\s+(\d+)\b}).captures.first.to_i
+    headers = Hash[ *raw_headers.split("\r\n").map { |h| h.split(/:\s+/, 2) }.flatten ]
+
+    [ status, headers, body ]
+  end
+  
+  def get(url)
+    if @server.backend.class == Backends::UnixServer
+      send_data("GET #{url} HTTP/1.1\r\nConnection: close\r\n\r\n")
+    else
+      Net::HTTP.get(URI.parse("http://#{@server.host}:#{@server.port}" + url))
+    end
+  end
+  
+  def post(url, params={})
+    Net::HTTP.post_form(URI.parse("http://#{@server.host}:#{@server.port}" + url), params).body
+  end
+end
+
+Spec::Runner.configure do |config|
+  config.include Matchers
+  config.include Helpers
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/announce.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/announce.rake
new file mode 100644 (file)
index 0000000..1b6a44e
--- /dev/null
@@ -0,0 +1,22 @@
+require 'erb'
+
+MSG_TEMPLATE = File.dirname(__FILE__) + '/email.erb'
+SEND_TO      = %w(thin-ruby@googlegroups.com ruby-talk@ruby-lang.org)
+
+desc 'Generate a template for the new version annoucement'
+task :ann do
+  msg = ERB.new(File.read(MSG_TEMPLATE)).result(binding)
+    
+  body = <<END_OF_MESSAGE
+To: #{SEND_TO.join(', ')}
+Subject: [ANN] Thin #{Thin::VERSION::STRING} #{Thin::VERSION::CODENAME} release
+
+#{msg}
+END_OF_MESSAGE
+
+  fork { `echo "#{body}" | mate` }
+end
+
+def changelog
+  File.read('CHANGELOG').split("==")[1].split("\n")[1..-1].join("\n")
+end
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/deploy.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/deploy.rake
new file mode 100644 (file)
index 0000000..68da6d6
--- /dev/null
@@ -0,0 +1,13 @@
+namespace :deploy do
+  task :site => %w(site:upload rdoc:upload)
+  
+  desc 'Deploy on rubyforge'
+  task :gem => %w(gem:upload_rubyforge deploy:site)
+end
+desc 'Deploy on all servers'
+task :deploy => "deploy:gem"
+
+def upload(file, to, options={})
+  sh %{ssh macournoyer@code.macournoyer.com "rm -rf code.macournoyer.com/#{to}"} if options[:replace]
+  sh %{scp -rq #{file} macournoyer@code.macournoyer.com:code.macournoyer.com/#{to}}
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/email.erb b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/email.erb
new file mode 100644 (file)
index 0000000..4cf7a85
--- /dev/null
@@ -0,0 +1,27 @@
+Hey,
+
+Thin version <%= Thin::VERSION::STRING %> (codename <%= Thin::VERSION::CODENAME %>) is out!
+
+== What's new?
+
+<%= changelog %>
+
+== Get it!
+Install Thin from RubyGems:
+
+ gem install thin
+
+== Contribute
+
+Site:            http://code.macournoyer.com/thin/
+Group:           http://groups.google.com/group/thin-ruby/topics
+Issues:          https://github.com/macournoyer/thin/issues
+Security issues: macournoyer+thinsecurity@gmail.com
+Code:            http://github.com/macournoyer/thin
+IRC:             #thin on freenode
+
+Thanks to all the people who contributed to Thin, EventMachine, Rack and Mongrel.
+
+Marc-Andre Cournoyer
+http://macournoyer.com/
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/gem.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/gem.rake
new file mode 100644 (file)
index 0000000..5e6478a
--- /dev/null
@@ -0,0 +1,66 @@
+require 'rake/gempackagetask'
+require 'yaml'
+
+task :clean => :clobber_package
+
+Thin::GemSpec = Gem::Specification.new do |s|
+  s.name                  = Thin::NAME
+  s.version               = Thin::VERSION::STRING
+  s.platform              = WIN ? Gem::Platform::CURRENT : Gem::Platform::RUBY
+  s.summary               = 
+  s.description           = "A thin and fast web server"
+  s.author                = "Marc-Andre Cournoyer"
+  s.email                 = 'macournoyer@gmail.com'
+  s.homepage              = 'http://code.macournoyer.com/thin/'
+  s.rubyforge_project     = 'thin'
+  s.has_rdoc              = true
+  s.executables           = %w(thin)
+
+  s.required_ruby_version = '>= 1.8.5'
+  
+  s.add_dependency        'rack',         '>= 1.0.0'
+  s.add_dependency        'eventmachine', '>= 0.12.6'
+  unless WIN
+    s.add_dependency      'daemons',      '>= 1.0.9'
+  end
+
+  s.files                 = %w(COPYING CHANGELOG README Rakefile) +
+                            Dir.glob("{benchmark,bin,doc,example,lib,spec,tasks}/**/*") - Dir.glob("lib/thin_parser.*") + 
+                            Dir.glob("ext/**/*.{h,c,rb,rl}")
+  
+  if WIN
+    s.files              += FileList["lib/*/thin_parser.*"].to_a
+  else
+    s.extensions          = FileList["ext/**/extconf.rb"].to_a
+  end
+  
+  s.require_path          = "lib"
+  s.bindir                = "bin"
+end
+
+Rake::GemPackageTask.new(Thin::GemSpec) do |p|
+  p.gem_spec = Thin::GemSpec
+end
+
+task :tag_warn do
+  puts "*" * 40
+  puts "Don't forget to tag the release:"
+  puts
+  puts "  git tag -m 'Tagging #{Thin::SERVER}' -a v#{Thin::VERSION::STRING}"
+  puts
+  puts "or run rake tag"
+  puts "*" * 40
+end
+task :tag do
+  sh "git tag -m 'Tagging #{Thin::SERVER}' -a v#{Thin::VERSION::STRING}"
+end
+task :gem => :tag_warn
+
+namespace :gem do
+  desc 'Upload gems to gemcutter.org'
+  task :push do
+    Dir["pkg/#{Thin::GemSpec.full_name}*.gem"].each do |file|
+      sh "gem push #{file}"
+    end
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/rdoc.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/rdoc.rake
new file mode 100644 (file)
index 0000000..83efd80
--- /dev/null
@@ -0,0 +1,25 @@
+require 'rake/rdoctask'
+
+CLEAN.include %w(doc/rdoc)
+
+Rake::RDocTask.new do |rdoc|
+  rdoc.rdoc_dir = 'doc/rdoc'
+  rdoc.options += ['--quiet', '--title', Thin::NAME,
+                    "--opname", "index.html",
+                    "--line-numbers",
+                    "--main", "README",
+                    "--inline-source"]
+  rdoc.template = "site/rdoc.rb"
+  rdoc.main = "README"
+  rdoc.title = Thin::NAME
+  rdoc.rdoc_files.add %w(README) +
+                      FileList['lib/**/*.rb'] +
+                      FileList['bin/*']
+end
+
+namespace :rdoc do
+  desc 'Upload rdoc to code.macournoyer.com'
+  task :upload => :rdoc do
+    upload "doc/rdoc", 'thin/doc', :replace => true
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/site.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/site.rake
new file mode 100644 (file)
index 0000000..634565c
--- /dev/null
@@ -0,0 +1,15 @@
+namespace :site do
+  task :build do
+    mkdir_p 'tmp/site/images'
+    cd 'tmp/site' do
+      sh "SITE_ROOT='/thin' ruby ../../site/thin.rb --dump"
+    end
+    cp 'site/style.css', 'tmp/site'
+    cp_r Dir['site/images/*'], 'tmp/site/images'
+  end
+  
+  desc 'Upload website to code.macournoyer.com'
+  task :upload => 'site:build' do
+    upload 'tmp/site/*', 'thin'
+  end
+end
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/spec.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/spec.rake
new file mode 100644 (file)
index 0000000..7fac1b0
--- /dev/null
@@ -0,0 +1,44 @@
+CLEAN.include %w(coverage tmp log)
+
+gem "rspec", "~> 1.2.9"
+require 'spec/rake/spectask'
+
+PERF_SPECS = FileList['spec/perf/*_spec.rb']
+WIN_SPECS  = %w(
+  spec/backends/unix_server_spec.rb
+  spec/controllers/service_spec.rb
+  spec/daemonizing_spec.rb
+  spec/server/unix_socket_spec.rb
+  spec/server/swiftiply_spec.rb
+)
+# HACK Event machine causes some problems when running multiple
+# tests in the same VM so we split the specs in 2 before I find
+# a better solution...
+SPECS2     = %w(spec/server/threaded_spec.rb spec/server/tcp_spec.rb)  
+SPECS      = FileList['spec/**/*_spec.rb'] - PERF_SPECS - SPECS2
+
+def spec_task(name, specs)
+  Spec::Rake::SpecTask.new(name) do |t|
+    t.libs << 'lib'
+    t.spec_opts = %w(-fs -c)
+    t.spec_files = specs
+  end
+end
+
+desc "Run all examples"
+spec_task :spec, SPECS
+spec_task :spec2, SPECS2
+task :spec => [:compile, :spec2]
+
+desc "Run all performance examples"
+spec_task 'spec:perf', PERF_SPECS
+
+task :check_benchmark_unit_gem do
+  begin
+    require 'benchmark_unit'
+  rescue LoadError
+    abort "To run specs, install benchmark_unit gem"
+  end
+end
+
+task 'spec:perf' => :check_benchmark_unit_gem
\ No newline at end of file
diff --git a/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/stats.rake b/ruby/lib/ruby/gems/1.8/gems/thin-1.2.11-x86-mswin32/tasks/stats.rake
new file mode 100644 (file)
index 0000000..cdae843
--- /dev/null
@@ -0,0 +1,28 @@
+desc 'Show some stats about the code'
+task :stats do
+  line_count = proc do |path|
+    Dir[path].collect { |f| File.open(f).readlines.reject { |l| l =~ /(^\s*(\#|\/\*))|^\s*$/ }.size }.inject(0){ |sum,n| sum += n }
+  end
+  comment_count = proc do |path|
+    Dir[path].collect { |f| File.open(f).readlines.select { |l| l =~ /^\s*\#/ }.size }.inject(0) { |sum,n| sum += n }
+  end
+  lib     = line_count['lib/**/*.rb']
+  comment = comment_count['lib/**/*.rb']
+  ext     = line_count['ext/**/*.{c,h}'] 
+  spec    = line_count['spec/**/*.rb']
+  
+  comment_ratio = '%1.2f' % (comment.to_f / lib.to_f)
+  spec_ratio = '%1.2f' % (spec.to_f / lib.to_f)
+  
+  puts '/======================\\'
+  puts '| Part            LOC  |'
+  puts '|======================|'
+  puts "| lib             #{lib.to_s.ljust(5)}|"
+  puts "| lib comments    #{comment.to_s.ljust(5)}|"
+  puts "| ext             #{ext.to_s.ljust(5)}|"
+  puts "| spec            #{spec.to_s.ljust(5)}|"
+  puts '| ratios:              |'
+  puts "|   lib/comment   #{comment_ratio.to_s.ljust(5)}|"
+  puts "|   lib/spec      #{spec_ratio.to_s.ljust(5)}|"
+  puts '\======================/'
+end
diff --git a/ruby/lib/ruby/gems/1.8/specifications/daemons-1.1.4.gemspec b/ruby/lib/ruby/gems/1.8/specifications/daemons-1.1.4.gemspec
new file mode 100644 (file)
index 0000000..0e3d02f
--- /dev/null
@@ -0,0 +1,29 @@
+# -*- encoding: utf-8 -*-\r
+\r
+Gem::Specification.new do |s|\r
+  s.name = %q{daemons}\r
+  s.version = "1.1.4"\r
+\r
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=\r
+  s.authors = ["Thomas Uehlinger"]\r
+  s.date = %q{2011-06-17}\r
+  s.description = %q{Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server)  to be run as a daemon and to be controlled by simple start/stop/restart commands.  You can also call blocks as daemons and control them from the parent or just daemonize the current process.  Besides this basic functionality, daemons offers many advanced features like exception  backtracing and logging (in case your ruby script crashes) and monitoring and automatic restarting of your processes if they crash.}\r
+  s.email = %q{th.uehlinger@gmx.ch}\r
+  s.extra_rdoc_files = ["README", "Releases", "TODO"]\r
+  s.files = ["Rakefile", "Releases", "TODO", "README", "LICENSE", "setup.rb", "lib/daemons.rb", "lib/daemons/monitor.rb", "lib/daemons/application_group.rb", "lib/daemons/etc_extension.rb", "lib/daemons/daemonize.rb", "lib/daemons/cmdline.rb", "lib/daemons/controller.rb", "lib/daemons/change_privilege.rb", "lib/daemons/application.rb", "lib/daemons/pidmem.rb", "lib/daemons/pid.rb", "lib/daemons/pidfile.rb", "lib/daemons/exceptions.rb", "examples/run/ctrl_normal.rb", "examples/run/ctrl_monitor.rb", "examples/run/ctrl_proc.rb", "examples/run/ctrl_multiple.rb", "examples/run/ctrl_optionparser.rb", "examples/run/myserver.rb", "examples/run/myserver_hanging.rb", "examples/run/ctrl_exit.rb", "examples/run/myserver_crashing.rb", "examples/run/ctrl_proc_multiple.rb", "examples/run/ctrl_keep_pid_files.rb", "examples/run/ctrl_proc_simple.rb", "examples/run/ctrl_ontop.rb", "examples/run/ctrl_crash.rb", "examples/run/ctrl_exec.rb", "examples/run/ctrl_hanging.rb", "examples/run/myserver_exiting.rb", "examples/call/call.rb", "examples/call/call_monitor.rb", "examples/daemonize/daemonize.rb"]\r
+  s.homepage = %q{http://daemons.rubyforge.org}\r
+  s.require_paths = ["lib"]\r
+  s.rubyforge_project = %q{daemons}\r
+  s.rubygems_version = %q{1.3.5}\r
+  s.summary = %q{A toolkit to create and control daemons in different ways}\r
+\r
+  if s.respond_to? :specification_version then\r
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION\r
+    s.specification_version = 2\r
+\r
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then\r
+    else\r
+    end\r
+  else\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/specifications/eventmachine-0.12.10-x86-mswin32-60.gemspec b/ruby/lib/ruby/gems/1.8/specifications/eventmachine-0.12.10-x86-mswin32-60.gemspec
new file mode 100644 (file)
index 0000000..55a3526
--- /dev/null
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-\r
+\r
+Gem::Specification.new do |s|\r
+  s.name = %q{eventmachine}\r
+  s.version = "0.12.10"\r
+  s.platform = %q{x86-mswin32-60}\r
+\r
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=\r
+  s.authors = ["Francis Cianfrocca"]\r
+  s.date = %q{2009-10-25}\r
+  s.description = %q{EventMachine implements a fast, single-threaded engine for arbitrary network communications. It's extremely easy to use in Ruby. EventMachine wraps all interactions with IP sockets, allowing programs to concentrate on the implementation of network protocols. It can be used to create both network servers and clients. To create a server or client, a Ruby program only needs to specify the IP address and port, and provide a Module that implements the communications protocol. Implementations of several standard network protocols are provided with the package, primarily to serve as examples. The real goal of EventMachine is to enable programs to easily interface with other programs using TCP/IP, especially if custom protocols are required.}\r
+  s.email = %q{garbagecat10@gmail.com}\r
+  s.files = [".gitignore", "README", "Rakefile", "docs/COPYING", "docs/ChangeLog", "docs/DEFERRABLES", "docs/EPOLL", "docs/GNU", "docs/INSTALL", "docs/KEYBOARD", "docs/LEGAL", "docs/LIGHTWEIGHT_CONCURRENCY", "docs/PURE_RUBY", "docs/RELEASE_NOTES", "docs/SMTP", "docs/SPAWNED_PROCESSES", "docs/TODO", "eventmachine.gemspec", "examples/ex_channel.rb", "examples/ex_queue.rb", "examples/helper.rb", "ext/binder.cpp", "ext/binder.h", "ext/cmain.cpp", "ext/cplusplus.cpp", "ext/ed.cpp", "ext/ed.h", "ext/em.cpp", "ext/em.h", "ext/emwin.cpp", "ext/emwin.h", "ext/epoll.cpp", "ext/epoll.h", "ext/eventmachine.h", "ext/eventmachine_cpp.h", "ext/extconf.rb", "ext/fastfilereader/extconf.rb", "ext/fastfilereader/mapper.cpp", "ext/fastfilereader/mapper.h", "ext/fastfilereader/rubymain.cpp", "ext/files.cpp", "ext/files.h", "ext/kb.cpp", "ext/page.cpp", "ext/page.h", "ext/pipe.cpp", "ext/project.h", "ext/rubymain.cpp", "ext/sigs.cpp", "ext/sigs.h", "ext/ssl.cpp", "ext/ssl.h", "java/.classpath", "java/.project", "java/src/com/rubyeventmachine/EmReactor.java", "java/src/com/rubyeventmachine/EmReactorException.java", "java/src/com/rubyeventmachine/EventableChannel.java", "java/src/com/rubyeventmachine/EventableDatagramChannel.java", "java/src/com/rubyeventmachine/EventableSocketChannel.java", "java/src/com/rubyeventmachine/application/Application.java", "java/src/com/rubyeventmachine/application/Connection.java", "java/src/com/rubyeventmachine/application/ConnectionFactory.java", "java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java", "java/src/com/rubyeventmachine/application/PeriodicTimer.java", "java/src/com/rubyeventmachine/application/Timer.java", "java/src/com/rubyeventmachine/tests/ApplicationTest.java", "java/src/com/rubyeventmachine/tests/ConnectTest.java", "java/src/com/rubyeventmachine/tests/EMTest.java", "java/src/com/rubyeventmachine/tests/TestDatagrams.java", "java/src/com/rubyeventmachine/tests/TestServers.java", "java/src/com/rubyeventmachine/tests/TestTimers.java", "lib/em/buftok.rb", "lib/em/callback.rb", "lib/em/channel.rb", "lib/em/connection.rb", "lib/em/deferrable.rb", "lib/em/file_watch.rb", "lib/em/future.rb", "lib/em/messages.rb", "lib/em/process_watch.rb", "lib/em/processes.rb", "lib/em/protocols.rb", "lib/em/protocols/header_and_content.rb", "lib/em/protocols/httpclient.rb", "lib/em/protocols/httpclient2.rb", "lib/em/protocols/line_and_text.rb", "lib/em/protocols/linetext2.rb", "lib/em/protocols/memcache.rb", "lib/em/protocols/object_protocol.rb", "lib/em/protocols/postgres3.rb", "lib/em/protocols/saslauth.rb", "lib/em/protocols/smtpclient.rb", "lib/em/protocols/smtpserver.rb", "lib/em/protocols/socks4.rb", "lib/em/protocols/stomp.rb", "lib/em/protocols/tcptest.rb", "lib/em/queue.rb", "lib/em/spawnable.rb", "lib/em/streamer.rb", "lib/em/timers.rb", "lib/em/version.rb", "lib/eventmachine.rb", "lib/evma.rb", "lib/evma/callback.rb", "lib/evma/container.rb", "lib/evma/factory.rb", "lib/evma/protocol.rb", "lib/evma/reactor.rb", "lib/jeventmachine.rb", "lib/pr_eventmachine.rb", "setup.rb", "tasks/cpp.rake_example", "tests/client.crt", "tests/client.key", "tests/test_attach.rb", "tests/test_basic.rb", "tests/test_channel.rb", "tests/test_connection_count.rb", "tests/test_defer.rb", "tests/test_epoll.rb", "tests/test_error_handler.rb", "tests/test_errors.rb", "tests/test_exc.rb", "tests/test_file_watch.rb", "tests/test_futures.rb", "tests/test_get_sock_opt.rb", "tests/test_handler_check.rb", "tests/test_hc.rb", "tests/test_httpclient.rb", "tests/test_httpclient2.rb", "tests/test_inactivity_timeout.rb", "tests/test_kb.rb", "tests/test_ltp.rb", "tests/test_ltp2.rb", "tests/test_next_tick.rb", "tests/test_object_protocol.rb", "tests/test_pause.rb", "tests/test_pending_connect_timeout.rb", "tests/test_process_watch.rb", "tests/test_processes.rb", "tests/test_proxy_connection.rb", "tests/test_pure.rb", "tests/test_queue.rb", "tests/test_running.rb", "tests/test_sasl.rb", "tests/test_send_file.rb", "tests/test_servers.rb", "tests/test_smtpclient.rb", "tests/test_smtpserver.rb", "tests/test_spawn.rb", "tests/test_ssl_args.rb", "tests/test_ssl_methods.rb", "tests/test_ssl_verify.rb", "tests/test_timers.rb", "tests/test_ud.rb", "tests/testem.rb", "web/whatis", "lib/rubyeventmachine.so", "lib/fastfilereaderext.so"]\r
+  s.homepage = %q{http://rubyeventmachine.com}\r
+  s.rdoc_options = ["--title", "EventMachine", "--main", "README", "--line-numbers", "-x", "lib/em/version", "-x", "lib/emva", "-x", "lib/evma/", "-x", "lib/pr_eventmachine", "-x", "lib/jeventmachine"]\r
+  s.require_paths = ["lib"]\r
+  s.rubyforge_project = %q{eventmachine}\r
+  s.rubygems_version = %q{1.3.5}\r
+  s.summary = %q{Ruby/EventMachine library}\r
+\r
+  if s.respond_to? :specification_version then\r
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION\r
+    s.specification_version = 2\r
+\r
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then\r
+    else\r
+    end\r
+  else\r
+  end\r
+end\r
diff --git a/ruby/lib/ruby/gems/1.8/specifications/mongrel-1.1.5-x86-mswin32-60.gemspec b/ruby/lib/ruby/gems/1.8/specifications/mongrel-1.1.5-x86-mswin32-60.gemspec
deleted file mode 100644 (file)
index 41ab8da..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- encoding: utf-8 -*-\r
-\r
-Gem::Specification.new do |s|\r
-  s.name = %q{mongrel}\r
-  s.version = "1.1.5"\r
-  s.platform = %q{x86-mswin32-60}\r
-\r
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=\r
-  s.authors = ["Zed A. Shaw"]\r
-  s.date = %q{2008-05-22}\r
-  s.default_executable = %q{mongrel_rails}\r
-  s.description = %q{A small fast HTTP library and server that runs Rails, Camping, Nitro and Iowa apps.}\r
-  s.email = %q{}\r
-  s.executables = ["mongrel_rails"]\r
-  s.files = ["bin/mongrel_rails", "CHANGELOG", "COPYING", "examples/builder.rb", "examples/camping/blog.rb", "examples/camping/README", "examples/camping/tepee.rb", "examples/httpd.conf", "examples/mime.yaml", "examples/mongrel.conf", "examples/mongrel_simple_ctrl.rb", "examples/mongrel_simple_service.rb", "examples/monitrc", "examples/random_thrash.rb", "examples/simpletest.rb", "examples/webrick_compare.rb", "ext/http11/ext_help.h", "ext/http11/extconf.rb", "ext/http11/http11.c", "ext/http11/http11_parser.c", "ext/http11/http11_parser.h", "ext/http11/http11_parser.java.rl", "ext/http11/http11_parser.rl", "ext/http11/http11_parser_common.rl", "ext/http11_java/Http11Service.java", "ext/http11_java/org/jruby/mongrel/Http11.java", "ext/http11_java/org/jruby/mongrel/Http11Parser.java", "lib/mongrel/camping.rb", "lib/mongrel/cgi.rb", "lib/mongrel/command.rb", "lib/mongrel/configurator.rb", "lib/mongrel/const.rb", "lib/mongrel/debug.rb", "lib/mongrel/gems.rb", "lib/mongrel/handlers.rb", "lib/mongrel/header_out.rb", "lib/mongrel/http_request.rb", "lib/mongrel/http_response.rb", "lib/mongrel/init.rb", "lib/mongrel/mime_types.yml", "lib/mongrel/rails.rb", "lib/mongrel/stats.rb", "lib/mongrel/tcphack.rb", "lib/mongrel/uri_classifier.rb", "lib/mongrel.rb", "LICENSE", "Manifest", "mongrel-public_cert.pem", "mongrel.gemspec", "README", "setup.rb", "test/mime.yaml", "test/mongrel.conf", "test/test_cgi_wrapper.rb", "test/test_command.rb", "test/test_conditional.rb", "test/test_configurator.rb", "test/test_debug.rb", "test/test_handlers.rb", "test/test_http11.rb", "test/test_redirect_handler.rb", "test/test_request_progress.rb", "test/test_response.rb", "test/test_stats.rb", "test/test_uriclassifier.rb", "test/test_ws.rb", "test/testhelp.rb", "TODO", "tools/trickletest.rb", "lib/http11.so"]\r
-  s.homepage = %q{http://mongrel.rubyforge.org}\r
-  s.require_paths = ["lib", "ext"]\r
-  s.required_ruby_version = Gem::Requirement.new(">= 1.8.4")\r
-  s.rubyforge_project = %q{mongrel}\r
-  s.rubygems_version = %q{1.3.5}\r
-  s.summary = %q{A small fast HTTP library and server that runs Rails, Camping, Nitro and Iowa apps.}\r
-  s.test_files = ["test/test_cgi_wrapper.rb", "test/test_command.rb", "test/test_conditional.rb", "test/test_configurator.rb", "test/test_debug.rb", "test/test_handlers.rb", "test/test_http11.rb", "test/test_redirect_handler.rb", "test/test_request_progress.rb", "test/test_response.rb", "test/test_stats.rb", "test/test_uriclassifier.rb", "test/test_ws.rb"]\r
-\r
-  if s.respond_to? :specification_version then\r
-    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION\r
-    s.specification_version = 2\r
-\r
-    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then\r
-      s.add_runtime_dependency(%q<gem_plugin>, [">= 0.2.3"])\r
-      s.add_runtime_dependency(%q<cgi_multipart_eof_fix>, [">= 2.4"])\r
-    else\r
-      s.add_dependency(%q<gem_plugin>, [">= 0.2.3"])\r
-      s.add_dependency(%q<cgi_multipart_eof_fix>, [">= 2.4"])\r
-    end\r
-  else\r
-    s.add_dependency(%q<gem_plugin>, [">= 0.2.3"])\r
-    s.add_dependency(%q<cgi_multipart_eof_fix>, [">= 2.4"])\r
-  end\r
-end\r
diff --git a/ruby/lib/ruby/gems/1.8/specifications/mongrel_service-0.3.4-x86-mswin32.gemspec b/ruby/lib/ruby/gems/1.8/specifications/mongrel_service-0.3.4-x86-mswin32.gemspec
deleted file mode 100644 (file)
index fe8d708..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- encoding: utf-8 -*-\r
-\r
-Gem::Specification.new do |s|\r
-  s.name = %q{mongrel_service}\r
-  s.version = "0.3.4"\r
-  s.platform = %q{i386-mswin32}\r
-\r
-  s.required_rubygems_version = nil if s.respond_to? :required_rubygems_version=\r
-  s.authors = ["Luis Lavena"]\r
-  s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDUDCCAjigAwIBAgIBADANBgkqhkiG9w0BAQUFADBOMRwwGgYDVQQDDBNtb25n\ncmVsLWRldmVsb3BtZW50MRkwFwYKCZImiZPyLGQBGRYJcnVieWZvcmdlMRMwEQYK\nCZImiZPyLGQBGRYDb3JnMB4XDTA3MDkxNjEwMzI0OVoXDTA4MDkxNTEwMzI0OVow\nTjEcMBoGA1UEAwwTbW9uZ3JlbC1kZXZlbG9wbWVudDEZMBcGCgmSJomT8ixkARkW\nCXJ1Ynlmb3JnZTETMBEGCgmSJomT8ixkARkWA29yZzCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAMb9v3B01eOHk3FyypbQgKXzJplUE5P6dXoG+xpPm0Lv\nP7BQmeMncOwqQ7zXpVQU+lTpXtQFTsOE3vL7KnhQFJKGvUAkbh24VFyopu1I0yqF\nmGu4nRqNXGXVj8TvLSj4S1WpSRLAa0acLPNyKhGmoV9+crqQypSjM6XKjBeppifo\n4eBmWGjiJEYMIJBvJZPJ4rAVDDA8C6CM1m3gMBGNh8ELDhU8HI9AP3dMIkTI2Wx9\n9xkJwHdroAaS0IFFtYChrwee4FbCF1FHDgoTosMwa47DrLHg4hZ6ojaKwK5QVWEV\nXGb6ju5UqpktnSWF2W+Lvl/K0tI42OH2CAhebT1gEVUCAwEAAaM5MDcwCQYDVR0T\nBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFGHChyMSZ16u9WOzKhgJSQ9lqDc5\nMA0GCSqGSIb3DQEBBQUAA4IBAQA/lfeN2WdB1xN+82tT7vNS4HOjRQw6MUh5yktu\nGQjaGqm0UB+aX0Z9y0B0qpfv9rj7nmIvEGiwBmDepNWYCGuW15JyqpN7QVVnG2xS\nMrame7VqgjM7A+VGDD5In5LtWbM/CHAATvvFlQ5Ph13YE1EdnVbZ65c+KQv+5sFY\nQ+zEop74d878uaC/SAHHXS46TiXneocaLSYw1CEZs/MAIy+9c4Q5ESbGpgnfg1Ad\n6lwl7k3hsNHO/+tZzx4HJtOXDI1yAl3+q6T9J0yI3z97EinwvAKhS1eyOI2Y5eeT\ntbQaNYkU127B3l/VNpd8fQm3Jkl/PqCCmDBQjUszFrJEODug\n-----END CERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\nMIIDQzCCAiugAwIBAgIBADANBgkqhkiG9w0BAQUFADBOMRwwGgYDVQQDDBNtb25n\ncmVsLWRldmVsb3BtZW50MRkwFwYKCZImiZPyLGQBGRYJcnVieWZvcmdlMRMwEQYK\nCZImiZPyLGQBGRYDb3JnMB4XDTA3MDkyNDAyMzAzOVoXDTA4MDkyMzAyMzAzOVow\nQTETMBEGA1UEAwwKbHVpc2xhdmVuYTEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMw\nEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEA5gIWDL7FYk43BEM3guuNz1vrr2RW7UAh+KY8w2+cGxt+MNi3P+rrfx/LAr7d\nKKV+RT7PQgutb9MZhk8ghn0qgTpCxWbvlhnFrMqIDaJy9nXGEWt6M0fTWdEojrzF\ndLSO//3GYzZze/ykE20KeNZ8SXzbxPwmIwrKfvoGYshGdmU7RAwiV6vV9ZS4uB1I\n/pvzsWp3cSqZwxJvtb7P445jcDMPqFXjMFVAXd6KpfVNl4ZNBzYHc0Ii2uLu3Pv4\njOmwvJlYZ30ccBh0G86ngtlOpZNrdcnXgRKXADGnxPickYZMdIHPPykIeR6Im0sR\nhCq6hsHkuWQU6CuwqTtGErw6uwIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQE\nAwIEsDAdBgNVHQ4EFgQUkz5QYICmPVaUs8rhPOQmhrDosCUwDQYJKoZIhvcNAQEF\nBQADggEBAMVgi+dtUNn1WNgsrkDi8wPkBbTj4qvTdu/mLncUD/wSNEfaf0ADiycu\nW9x8imY3UmSyyc2HO+/Wnf6/PkbaoAcUZxl/7a60UDqSyTpdpfB3moA7z9+j+Kyp\nnVFr7JVQG3SSNC/y0XrYf6J0DCgpLwOqLSes/KaQOtJlXl/xV37AHXEnAPjwJyCD\nqafzGwoObqIf7Vj8IzU/eoCx/SxAYnaevry1bls58eb28FKGuq7bWyJuDrFu1H8m\nmPG0Kv6rJ8vKyQiYZMAKm+XpuDZ09rnGm3ytdeEpXDfF5osSDDBSlWox82z2ulMd\nknPjAuGZt50yNl1teBnVEZkty2RINfo=\n-----END CERTIFICATE-----\n"]\r
-  s.date = %q{2008-01-02}\r
-  s.description = %q{This plugin offer native win32 services for rails, powered by Mongrel.}\r
-  s.email = %q{}\r
-  s.files = ["bin/mongrel_service.exe", "tools/freebasic.rb", "TODO", "resources/defaults.yaml", "README", "native/mongrel_service.bi", "native/mongrel_service.bas", "native/console_process.bi", "native/console_process.bas", "native/_debug.bi", "LICENSE", "lib/ServiceFB/ServiceFB_Utils.bi", "lib/ServiceFB/ServiceFB_Utils.bas", "lib/ServiceFB/ServiceFB.bi", "lib/ServiceFB/ServiceFB.bas", "lib/ServiceFB/_utils_internals.bi", "lib/ServiceFB/_internals.bi", "lib/mongrel_service/init.rb", "COPYING", "CHANGELOG", "Manifest", "mongrel_service.gemspec"]\r
-  s.homepage = %q{}\r
-  s.require_paths = ["lib"]\r
-  s.required_ruby_version = Gem::Requirement.new("> 0.0.0")\r
-  s.rubyforge_project = %q{mongrel_service}\r
-  s.rubygems_version = %q{1.3.5}\r
-  s.summary = %q{Mongrel Native Win32 Service Plugin for Rails}\r
-\r
-  if s.respond_to? :specification_version then\r
-    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION\r
-    s.specification_version = 1\r
-\r
-    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then\r
-      s.add_runtime_dependency(%q<gem_plugin>, [">= 0.2.3", "< 0.3.0"])\r
-      s.add_runtime_dependency(%q<mongrel>, [">= 1.0.2", "< 1.2.0"])\r
-      s.add_runtime_dependency(%q<win32-service>, [">= 0.5.2", "< 0.6.0"])\r
-    else\r
-      s.add_dependency(%q<gem_plugin>, [">= 0.2.3", "< 0.3.0"])\r
-      s.add_dependency(%q<mongrel>, [">= 1.0.2", "< 1.2.0"])\r
-      s.add_dependency(%q<win32-service>, [">= 0.5.2", "< 0.6.0"])\r
-    end\r
-  else\r
-    s.add_dependency(%q<gem_plugin>, [">= 0.2.3", "< 0.3.0"])\r
-    s.add_dependency(%q<mongrel>, [">= 1.0.2", "< 1.2.0"])\r
-    s.add_dependency(%q<win32-service>, [">= 0.5.2", "< 0.6.0"])\r
-  end\r
-end\r
diff --git a/ruby/lib/ruby/gems/1.8/specifications/thin-1.2.11-x86-mswin32.gemspec b/ruby/lib/ruby/gems/1.8/specifications/thin-1.2.11-x86-mswin32.gemspec
new file mode 100644 (file)
index 0000000..745dd29
--- /dev/null
@@ -0,0 +1,41 @@
+# -*- encoding: utf-8 -*-\r
+\r
+Gem::Specification.new do |s|\r
+  s.name = %q{thin}\r
+  s.version = "1.2.11"\r
+  s.platform = %q{x86-mswin32}\r
+\r
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=\r
+  s.authors = ["Marc-Andre Cournoyer"]\r
+  s.date = %q{2011-03-28}\r
+  s.default_executable = %q{thin}\r
+  s.description = %q{A thin and fast web server}\r
+  s.email = %q{macournoyer@gmail.com}\r
+  s.executables = ["thin"]\r
+  s.files = ["COPYING", "CHANGELOG", "README", "Rakefile", "benchmark/abc", "benchmark/benchmarker.rb", "benchmark/runner", "bin/thin", "example/adapter.rb", "example/async_app.ru", "example/async_chat.ru", "example/async_tailer.ru", "example/config.ru", "example/monit_sockets", "example/monit_unixsock", "example/myapp.rb", "example/ramaze.ru", "example/thin.god", "example/thin_solaris_smf.erb", "example/thin_solaris_smf.readme.txt", "example/vlad.rake", "lib/rack/adapter/loader.rb", "lib/rack/adapter/rails.rb", "lib/thin/backends/base.rb", "lib/thin/backends/swiftiply_client.rb", "lib/thin/backends/tcp_server.rb", "lib/thin/backends/unix_server.rb", "lib/thin/command.rb", "lib/thin/connection.rb", "lib/thin/controllers/cluster.rb", "lib/thin/controllers/controller.rb", "lib/thin/controllers/service.rb", "lib/thin/controllers/service.sh.erb", "lib/thin/daemonizing.rb", "lib/thin/headers.rb", "lib/thin/logging.rb", "lib/thin/request.rb", "lib/thin/response.rb", "lib/thin/runner.rb", "lib/thin/server.rb", "lib/thin/stats.html.erb", "lib/thin/stats.rb", "lib/thin/statuses.rb", "lib/thin/version.rb", "lib/thin.rb", "spec/backends/swiftiply_client_spec.rb", "spec/backends/tcp_server_spec.rb", "spec/backends/unix_server_spec.rb", "spec/command_spec.rb", "spec/configs/cluster.yml", "spec/configs/single.yml", "spec/connection_spec.rb", "spec/controllers/cluster_spec.rb", "spec/controllers/controller_spec.rb", "spec/controllers/service_spec.rb", "spec/daemonizing_spec.rb", "spec/headers_spec.rb", "spec/logging_spec.rb", "spec/perf/request_perf_spec.rb", "spec/perf/response_perf_spec.rb", "spec/perf/server_perf_spec.rb", "spec/rack/loader_spec.rb", "spec/rack/rails_adapter_spec.rb", "spec/rails_app/app/controllers/application.rb", "spec/rails_app/app/controllers/simple_controller.rb", "spec/rails_app/app/helpers/application_helper.rb", "spec/rails_app/app/views/simple/index.html.erb", "spec/rails_app/config/boot.rb", "spec/rails_app/config/environment.rb", "spec/rails_app/config/environments/development.rb", "spec/rails_app/config/environments/production.rb", "spec/rails_app/config/environments/test.rb", "spec/rails_app/config/initializers/inflections.rb", "spec/rails_app/config/initializers/mime_types.rb", "spec/rails_app/config/routes.rb", "spec/rails_app/public/404.html", "spec/rails_app/public/422.html", "spec/rails_app/public/500.html", "spec/rails_app/public/dispatch.cgi", "spec/rails_app/public/dispatch.fcgi", "spec/rails_app/public/dispatch.rb", "spec/rails_app/public/favicon.ico", "spec/rails_app/public/images/rails.png", "spec/rails_app/public/index.html", "spec/rails_app/public/javascripts/application.js", "spec/rails_app/public/javascripts/controls.js", "spec/rails_app/public/javascripts/dragdrop.js", "spec/rails_app/public/javascripts/effects.js", "spec/rails_app/public/javascripts/prototype.js", "spec/rails_app/public/robots.txt", "spec/rails_app/script/about", "spec/rails_app/script/console", "spec/rails_app/script/destroy", "spec/rails_app/script/generate", "spec/rails_app/script/performance/benchmarker", "spec/rails_app/script/performance/profiler", "spec/rails_app/script/performance/request", "spec/rails_app/script/plugin", "spec/rails_app/script/process/inspector", "spec/rails_app/script/process/reaper", "spec/rails_app/script/process/spawner", "spec/rails_app/script/runner", "spec/rails_app/script/server", "spec/request/mongrel_spec.rb", "spec/request/parser_spec.rb", "spec/request/persistent_spec.rb", "spec/request/processing_spec.rb", "spec/response_spec.rb", "spec/runner_spec.rb", "spec/server/builder_spec.rb", "spec/server/pipelining_spec.rb", "spec/server/robustness_spec.rb", "spec/server/stopping_spec.rb", "spec/server/swiftiply.yml", "spec/server/swiftiply_spec.rb", "spec/server/tcp_spec.rb", "spec/server/threaded_spec.rb", "spec/server/unix_socket_spec.rb", "spec/server_spec.rb", "spec/spec_helper.rb", "tasks/announce.rake", "tasks/deploy.rake", "tasks/email.erb", "tasks/gem.rake", "tasks/rdoc.rake", "tasks/site.rake", "tasks/spec.rake", "tasks/stats.rake", "ext/thin_parser/ext_help.h", "ext/thin_parser/parser.h", "ext/thin_parser/parser.c", "ext/thin_parser/thin.c", "ext/thin_parser/extconf.rb", "ext/thin_parser/common.rl", "ext/thin_parser/parser.rl", "lib/1.8/thin_parser.so", "lib/1.9/thin_parser.so"]\r
+  s.homepage = %q{http://code.macournoyer.com/thin/}\r
+  s.require_paths = ["lib"]\r
+  s.required_ruby_version = Gem::Requirement.new(">= 1.8.5")\r
+  s.rubyforge_project = %q{thin}\r
+  s.rubygems_version = %q{1.3.5}\r
+  s.summary = %q{A thin and fast web server}\r
+\r
+  if s.respond_to? :specification_version then\r
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION\r
+    s.specification_version = 3\r
+\r
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then\r
+      s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])\r
+      s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.6"])\r
+      s.add_runtime_dependency(%q<daemons>, [">= 1.0.9"])\r
+    else\r
+      s.add_dependency(%q<rack>, [">= 1.0.0"])\r
+      s.add_dependency(%q<eventmachine>, [">= 0.12.6"])\r
+      s.add_dependency(%q<daemons>, [">= 1.0.9"])\r
+    end\r
+  else\r
+    s.add_dependency(%q<rack>, [">= 1.0.0"])\r
+    s.add_dependency(%q<eventmachine>, [">= 0.12.6"])\r
+    s.add_dependency(%q<daemons>, [">= 1.0.9"])\r
+  end\r
+end\r
diff --git a/script/mongrel_rails_env b/script/mongrel_rails_env
deleted file mode 100644 (file)
index 35174e7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-home = File.dirname(Dir.pwd)\r
-ENV['PATH'] = "#{home}/imagemagick;#{home}/subversion/bin;#{home}/sqlite;" + ENV['PATH']\r
-ENV['MAGICK_CODER_MODULE_PATH'] = "#{home}/imagemagick/modules/coders"\r
index baf7819..8a42eef 100644 (file)
@@ -21,7 +21,7 @@ module Command
       end\r
     }\r
 \r
-    wrapper = File.join(PACKAGE_HOME, "script/wrapper.bat")\r
+    wrapper = File.join(PACKAGE_HOME, "script/service/wrapper.bat")\r
 \r
     Service.config.each {|key, conf|\r
       name = conf["service_name"]\r
@@ -35,13 +35,11 @@ module Command
           raise unless system(%Q[sc config "#{name}" binpath= "\\"#{wrapper}\\" httpd -k runservice"])\r
           next\r
         when :redmine\r
-          redmine = File.join(PACKAGE_HOME, "redmine")\r
-          script = File.join(PACKAGE_HOME, "script/mongrel_rails_env")\r
-          raise unless system(%Q[mongrel_rails service::install -N "#{name}" -c "#{redmine}" -p #{port} -e production --prefix /redmine -S "#{script}"])\r
-          system(%Q[sc config "#{name}" start= auto])\r
+          exe = File.join(PACKAGE_HOME, "script/service/redmine.exe")\r
+          raise unless system(%Q[sc create "#{name}" binpath= "\\"#{wrapper}\\" \\"#{exe}\\"" start= auto])\r
         when :jenkins\r
-          exe = File.join(PACKAGE_HOME, "jenkins/jenkins.exe")\r
-          raise unless system(%Q[sc create #{name} binpath= "\\"#{wrapper}\\" \\"#{exe}\\"" start= auto])\r
+          exe = File.join(PACKAGE_HOME, "script/service/jenkins.exe")\r
+          raise unless system(%Q[sc create "#{name}" binpath= "\\"#{wrapper}\\" \\"#{exe}\\"" start= auto])\r
         when :opends\r
           opends = File.join(PACKAGE_HOME, "opends")\r
           exe = File.join(opends, "lib/opends_service.exe")\r
diff --git a/script/service/redmine.exe b/script/service/redmine.exe
new file mode 100644 (file)
index 0000000..7fd8dc6
Binary files /dev/null and b/script/service/redmine.exe differ
diff --git a/script/service/wrapper.bat b/script/service/wrapper.bat
new file mode 100644 (file)
index 0000000..4a0d9cf
--- /dev/null
@@ -0,0 +1,4 @@
+@echo off\r
+setlocal\r
+call "%~dp0..\setenv.bat"\r
+%*\r
index 0001dc9..b585e7d 100644 (file)
@@ -1,8 +1,12 @@
 @echo off\r
 set PACKAGE_HOME=%~dp0..\\r
-set ANT_HOME=%PACKAGE_HOME%ant\r
+\r
 set PATH=%PACKAGE_HOME%apache\bin;%PACKAGE_HOME%ruby\bin;%PACKAGE_HOME%sqlite;%PACKAGE_HOME%subversion\bin;%PACKAGE_HOME%imagemagick;%PACKAGE_HOME%opends\bat;%ANT_HOME%\bin;%PATH%\r
+\r
+set ANT_HOME=%PACKAGE_HOME%ant\r
 set APR_ICONV_PATH=%PACKAGE_HOME%apache\bin\iconv\r
+set JENKINS_HOME=%PACKAGE_HOME%jenkins\home\r
+set MAGICK_CODER_MODULE_PATH=%PACKAGE_HOME%imagemagick\modules\coders\r
 \r
 set RUBYOPT=rubygems\r
 set RUBYLIB=%PACKAGE_HOME%script\lib\r
diff --git a/script/wrapper.bat b/script/wrapper.bat
deleted file mode 100644 (file)
index 15263db..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off\r
-setlocal\r
-call "%~dp0setenv.bat"\r
-%*\r
diff --git a/template/jenkins/jenkins.xml.erb b/template/jenkins/jenkins.xml.erb
deleted file mode 100644 (file)
index bdcc1b1..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--\r
-The MIT License\r
-\r
-Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi\r
-\r
-Permission is hereby granted, free of charge, to any person obtaining a copy\r
-of this software and associated documentation files (the "Software"), to deal\r
-in the Software without restriction, including without limitation the rights\r
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-copies of the Software, and to permit persons to whom the Software is\r
-furnished to do so, subject to the following conditions:\r
-\r
-The above copyright notice and this permission notice shall be included in\r
-all copies or substantial portions of the Software.\r
-\r
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-THE SOFTWARE.\r
--->\r
-\r
-<!--\r
-  Windows service definition for Hudson\r
-\r
-  To uninstall, run "hudson.exe stop" to stop the service, then "hudson.exe uninstall" to uninstall the service.\r
-  Both commands don't produce any output if the execution is successful. \r
--->\r
-<service>\r
-  <id><%= jenkins_name %></id>\r
-  <name><%= jenkins_name %></name>\r
-  <description>This service runs Jenkins continuous integration system.</description>\r
-  <env name="JENKINS_HOME" value="%BASE%\home"/>\r
-  <!--\r
-    if you'd like to run Hudson with a specific version of Java, specify a full path to java.exe.\r
-    The following value assumes that you have java in your PATH.\r
-  -->\r
-  <executable>java</executable>\r
-  <arguments>-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --prefix=/jenkins --httpPort=<%= jenkins_port %></arguments>\r
-  <!--\r
-    interactive flag causes the empty black Java window to be displayed.\r
-    I'm still debugging this.\r
-  <interactive />\r
-  -->\r
-  <logmode>roll</logmode>\r
-       <logpath><%= File.join(home, "jenkins/log") %></logpath>\r
-</service>\r
diff --git a/template/script/service/jenkins.xml.erb b/template/script/service/jenkins.xml.erb
new file mode 100644 (file)
index 0000000..e5b6a22
--- /dev/null
@@ -0,0 +1,8 @@
+<service>\r
+  <id><%= jenkins_name %></id>\r
+  <name><%= jenkins_name %></name>\r
+  <executable>java</executable>\r
+  <arguments>-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "<%= File.join(home, "jenkins/jenkins.war") %>" --prefix=/jenkins --httpPort=<%= jenkins_port %></arguments>\r
+  <logmode>roll</logmode>\r
+       <logpath><%= File.join(home, "jenkins/log") %></logpath>\r
+</service>\r
diff --git a/template/script/service/redmine.xml.erb b/template/script/service/redmine.xml.erb
new file mode 100644 (file)
index 0000000..0f4c11a
--- /dev/null
@@ -0,0 +1,8 @@
+<service>\r
+  <id><%= redmine_name %></id>\r
+  <name><%= redmine_name %></name>\r
+  <executable>ruby</executable>\r
+  <arguments>-S thin -c "<%= redmine_root %>" -p <%= redmine_port %> -e production --prefix /redmine start</arguments>\r
+  <logmode>roll</logmode>\r
+  <logpath><%= File.join(redmine_root, "log") %></logpath>\r
+</service>\r