mongrel2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gemtest +0 -0
  3. data/History.rdoc +4 -0
  4. data/Manifest.txt +66 -0
  5. data/README.rdoc +169 -0
  6. data/Rakefile +77 -0
  7. data/bin/m2sh.rb +600 -0
  8. data/data/mongrel2/bootstrap.html +25 -0
  9. data/data/mongrel2/config.sql +84 -0
  10. data/data/mongrel2/mimetypes.sql +855 -0
  11. data/examples/README.txt +6 -0
  12. data/examples/config.rb +54 -0
  13. data/examples/helloworld-handler.rb +31 -0
  14. data/examples/request-dumper.rb +45 -0
  15. data/examples/request-dumper.tmpl +71 -0
  16. data/examples/run +17 -0
  17. data/lib/mongrel2.rb +62 -0
  18. data/lib/mongrel2/config.rb +212 -0
  19. data/lib/mongrel2/config/directory.rb +78 -0
  20. data/lib/mongrel2/config/dsl.rb +206 -0
  21. data/lib/mongrel2/config/handler.rb +124 -0
  22. data/lib/mongrel2/config/host.rb +88 -0
  23. data/lib/mongrel2/config/log.rb +48 -0
  24. data/lib/mongrel2/config/mimetype.rb +15 -0
  25. data/lib/mongrel2/config/proxy.rb +15 -0
  26. data/lib/mongrel2/config/route.rb +51 -0
  27. data/lib/mongrel2/config/server.rb +58 -0
  28. data/lib/mongrel2/config/setting.rb +15 -0
  29. data/lib/mongrel2/config/statistic.rb +23 -0
  30. data/lib/mongrel2/connection.rb +212 -0
  31. data/lib/mongrel2/constants.rb +159 -0
  32. data/lib/mongrel2/control.rb +165 -0
  33. data/lib/mongrel2/exceptions.rb +59 -0
  34. data/lib/mongrel2/handler.rb +309 -0
  35. data/lib/mongrel2/httprequest.rb +51 -0
  36. data/lib/mongrel2/httpresponse.rb +187 -0
  37. data/lib/mongrel2/jsonrequest.rb +43 -0
  38. data/lib/mongrel2/logging.rb +241 -0
  39. data/lib/mongrel2/mixins.rb +143 -0
  40. data/lib/mongrel2/request.rb +148 -0
  41. data/lib/mongrel2/response.rb +74 -0
  42. data/lib/mongrel2/table.rb +216 -0
  43. data/lib/mongrel2/xmlrequest.rb +36 -0
  44. data/spec/lib/constants.rb +237 -0
  45. data/spec/lib/helpers.rb +246 -0
  46. data/spec/lib/matchers.rb +50 -0
  47. data/spec/mongrel2/config/directory_spec.rb +91 -0
  48. data/spec/mongrel2/config/dsl_spec.rb +218 -0
  49. data/spec/mongrel2/config/handler_spec.rb +118 -0
  50. data/spec/mongrel2/config/host_spec.rb +30 -0
  51. data/spec/mongrel2/config/log_spec.rb +95 -0
  52. data/spec/mongrel2/config/proxy_spec.rb +30 -0
  53. data/spec/mongrel2/config/route_spec.rb +83 -0
  54. data/spec/mongrel2/config/server_spec.rb +84 -0
  55. data/spec/mongrel2/config/setting_spec.rb +30 -0
  56. data/spec/mongrel2/config/statistic_spec.rb +30 -0
  57. data/spec/mongrel2/config_spec.rb +111 -0
  58. data/spec/mongrel2/connection_spec.rb +172 -0
  59. data/spec/mongrel2/constants_spec.rb +32 -0
  60. data/spec/mongrel2/control_spec.rb +192 -0
  61. data/spec/mongrel2/handler_spec.rb +261 -0
  62. data/spec/mongrel2/httpresponse_spec.rb +232 -0
  63. data/spec/mongrel2/logging_spec.rb +76 -0
  64. data/spec/mongrel2/mixins_spec.rb +62 -0
  65. data/spec/mongrel2/request_spec.rb +157 -0
  66. data/spec/mongrel2/response_spec.rb +81 -0
  67. data/spec/mongrel2/table_spec.rb +176 -0
  68. data/spec/mongrel2_spec.rb +34 -0
  69. metadata +294 -0
  70. metadata.gz.sig +0 -0
@@ -0,0 +1,6 @@
1
+
2
+ This is a directory that contains some example Mongrel2 handlers, and
3
+ a configuration.
4
+
5
+ To start the example server, run the 'run' file in this directory and
6
+ point a browser to http://localhost:8113/.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # The Mongrel config used by the examples.
4
+
5
+ BEGIN {
6
+ require 'pathname'
7
+ basedir = Pathname( __FILE__ ).dirname.parent
8
+ libdir = basedir + 'lib'
9
+
10
+ $LOAD_PATH.unshift( libdir.to_s )
11
+ }
12
+
13
+ require 'rubygems'
14
+ require 'fileutils'
15
+ require 'mongrel2'
16
+ require 'mongrel2/config'
17
+
18
+ include FileUtils::Verbose
19
+
20
+ Mongrel2.log.level = Logger::INFO
21
+ Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
22
+ include Mongrel2::Config::DSL
23
+
24
+ Mongrel2::Config.init_database!
25
+
26
+ # the server to run them all
27
+ server '34D8E57C-3E91-4F24-9BBE-0B53C1827CB4' do
28
+
29
+ access_log "/logs/access.log"
30
+ error_log "/logs/error.log"
31
+ chroot "./"
32
+ pid_file "/run/mongrel2.pid"
33
+ default_host "localhost"
34
+ name "main"
35
+ port 8113
36
+
37
+ # your main host
38
+ host "localhost" do
39
+
40
+ route '/', directory( 'data/mongrel2/', 'bootstrap.html', 'text/html' )
41
+ route '/source', directory( 'examples/', 'README.txt', 'text/plain' )
42
+
43
+ # Handlers
44
+ route '/hello', handler( 'tcp://127.0.0.1:9999', 'helloworld-handler' )
45
+ route '/dump', handler( 'tcp://127.0.0.1:9997', 'request-dumper' )
46
+
47
+ end
48
+
49
+ end
50
+
51
+ setting "zeromq.threads", 2
52
+
53
+ mkdir_p 'run'
54
+ mkdir_p 'logs'
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'mongrel2/config'
5
+ require 'mongrel2/handler'
6
+
7
+ # A dumb, dead-simple example that just returns a plaintext 'Hello' document.
8
+ class HelloWorldHandler < Mongrel2::Handler
9
+
10
+ ### The main method to override -- accepts requests and returns responses.
11
+ def handle( request )
12
+ response = request.response
13
+
14
+ response.status = 200
15
+ response.headers.content_type = 'text/plain'
16
+ response.puts "Hello, world, it's #{Time.now}!"
17
+
18
+ return response
19
+ end
20
+
21
+ end # class HelloWorldHandler
22
+
23
+ # Log to a file instead of STDERR for a bit more speed.
24
+ Mongrel2.log = Logger.new( 'hello-world.log' )
25
+ Mongrel2.log.level = Logger::INFO
26
+
27
+ # Point to the config database, which will cause the handler to use
28
+ # its ID to look up its own socket info.
29
+ Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
30
+ HelloWorldHandler.run( 'helloworld-handler' )
31
+
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ require 'logger'
5
+ require 'mongrel2/config'
6
+ require 'mongrel2/handler'
7
+
8
+ require 'inversion'
9
+
10
+ # A handler that just dumps the request it gets from Mongrel2
11
+ class RequestDumper < Mongrel2::Handler
12
+
13
+ TEMPLATE_DIR = Pathname( __FILE__ ).dirname
14
+ Inversion::Template.configure( :template_paths => [TEMPLATE_DIR] )
15
+
16
+ ### Pre-load the template before running.
17
+ def initialize( * )
18
+ super
19
+ @template = Inversion::Template.load( 'request-dumper.tmpl' )
20
+ end
21
+
22
+
23
+ ### Handle a request
24
+ def handle( request )
25
+ response = request.response
26
+ template = @template.dup
27
+
28
+ template.request = request
29
+
30
+ response.status = 200
31
+ response.headers.content_type = 'text/html'
32
+ response.puts( template )
33
+
34
+ return response
35
+ end
36
+
37
+ end # class HelloWorldHandler
38
+
39
+ Mongrel2.log.level = Logger::INFO
40
+
41
+ # Point to the config database, which will cause the handler to use
42
+ # its ID to look up its own socket info.
43
+ Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
44
+ RequestDumper.run( 'request-dumper' )
45
+
@@ -0,0 +1,71 @@
1
+ <!doctype html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
5
+
6
+ <title><?attr title ?></title>
7
+ <meta name="description" content="Ruby-Mongrel2 request introspection handler">
8
+ <meta name="author" content="Michael Granger">
9
+
10
+ <meta name="viewport" content="width=device-width,initial-scale=1">
11
+
12
+ </head>
13
+
14
+ <body>
15
+
16
+ <header>
17
+ <h1>Request Dump (0x<?call "%0x" % request.object_id ?>)</h1>
18
+ </header>
19
+
20
+ <section id="dump">
21
+
22
+ <table>
23
+ <tr>
24
+ <th>Sender ID</th>
25
+ <td><?escape request.sender_id ?></td>
26
+ </tr>
27
+ <tr>
28
+ <th>Connection ID</th>
29
+ <td><?escape request.conn_id.to_s ?></td>
30
+ </tr>
31
+ </table>
32
+
33
+ <section id="headers">
34
+ <header>
35
+ <h1>Headers</h1>
36
+ </header>
37
+ <table>
38
+ <?for name, val in request.headers.each_header.each ?>
39
+ <tr>
40
+ <th><?attr name ?></th>
41
+ <td><?attr val ?></td>
42
+ </tr>
43
+ <?end for ?>
44
+ </table>
45
+ </section>
46
+
47
+ <section id="body">
48
+ <header>
49
+ <h1>Body</h1>
50
+ </header>
51
+
52
+ <p><code><?escape request.body.dump ?></code></p>
53
+ </section>
54
+
55
+ <section id="inspect">
56
+ <header>
57
+ <h1>Inspected Request</h1>
58
+ <p><tt>
59
+ <?pp request ?>
60
+ </tt></p>
61
+ </header>
62
+ </section>
63
+
64
+ </section>
65
+
66
+ <footer>
67
+ <p><tt>$Id$</tt></p>
68
+ </footer>
69
+
70
+ </body>
71
+ </html>
data/examples/run ADDED
@@ -0,0 +1,17 @@
1
+ #!/bin/sh
2
+
3
+ examplesdir=$(dirname $0)
4
+
5
+ echo "Creating the config database..."
6
+ ruby -rubygems -Ilib $examplesdir/config.rb
7
+
8
+ echo "Starting Mongrel2..."
9
+ m2sh start -db examples.sqlite -host localhost &
10
+
11
+ echo "Starting the 'helloworld' handler..."
12
+ ruby -rubygems -Ilib $examplesdir/helloworld-handler.rb &
13
+
14
+ echo "Starting the request dumper..."
15
+ ruby -rubygems -Ilib $examplesdir/request-dumper.rb &
16
+
17
+ echo "Okay, now point a browser to http://localhost:8113/."
data/lib/mongrel2.rb ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # A Mongrel2 connector and configuration library for Ruby.
5
+ #
6
+ # == Author/s
7
+ #
8
+ # * Michael Granger <ged@FaerieMUD.org>
9
+ #
10
+ module Mongrel2
11
+
12
+ abort "\n\n>>> Mongrel2 requires Ruby 1.9.2 or later. <<<\n\n" if RUBY_VERSION < '1.9.2'
13
+
14
+ # Library version constant
15
+ VERSION = '0.0.1'
16
+
17
+ # Version-control revision constant
18
+ REVISION = %q$Revision: be362e9b6bc8 $
19
+
20
+
21
+ require 'mongrel2/logging'
22
+ extend Mongrel2::Logging
23
+
24
+ require 'mongrel2/constants'
25
+ include Mongrel2::Constants
26
+
27
+
28
+ ### Get the library version. If +include_buildnum+ is true, the version string will
29
+ ### include the VCS rev ID.
30
+ def self::version_string( include_buildnum=false )
31
+ vstring = "Ruby-Mongrel2 %s" % [ VERSION ]
32
+ vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
33
+ return vstring
34
+ end
35
+
36
+
37
+ # ZMQ::Context (lazy-loaded)
38
+ @zmq_ctx = nil
39
+
40
+ ### Fetch the ZMQ::Context for sockets, creating it if necessary.
41
+ def self::zmq_context
42
+ if @zmq_ctx.nil?
43
+ Mongrel2.log.info "Using 0MQ %d.%d.%d" % ZMQ.version
44
+ @zmq_ctx = ZMQ::Context.new
45
+ end
46
+
47
+ return @zmq_ctx
48
+ end
49
+
50
+
51
+ require 'mongrel2/exceptions'
52
+ require 'mongrel2/connection'
53
+ require 'mongrel2/handler'
54
+ require 'mongrel2/request'
55
+ require 'mongrel2/httprequest'
56
+ require 'mongrel2/jsonrequest'
57
+ require 'mongrel2/xmlrequest'
58
+ require 'mongrel2/response'
59
+ require 'mongrel2/control'
60
+
61
+ end # module Mongrel2
62
+
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'pathname'
4
+ require 'uri'
5
+
6
+ require 'sequel'
7
+
8
+ begin
9
+ require 'configurability'
10
+ rescue LoadError
11
+ # No-op: it's optional
12
+ end
13
+
14
+ begin
15
+ require 'amalgalite'
16
+ # Rude hack to stop Sequel::Model from complaining if it's subclassed before
17
+ # the first database connection is established. Ugh.
18
+ Sequel::Model.db = Sequel.amalgalite if Sequel::DATABASES.empty?
19
+ rescue LoadError
20
+ require 'sqlite3'
21
+ Sequel::Model.db = Sequel.sqlite if Sequel::DATABASES.empty?
22
+ end
23
+
24
+
25
+ require 'mongrel2' unless defined?( Mongrel2 )
26
+ require 'mongrel2/mixins'
27
+
28
+ module Mongrel2
29
+
30
+ # The base Mongrel2 database-backed configuration class. It's a subclass of Sequel::Model, so
31
+ # you'll first need to be familiar with Sequel (http://sequel.rubyforge.org/) and
32
+ # especially its Sequel::Model ORM.
33
+ #
34
+ # You will also probably want to refer to the Sequel::Plugins documentation for the
35
+ # validation_helpers[http://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Plugins/ValidationHelpers.html]
36
+ # and
37
+ # subclasses[http://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Plugins/Subclasses.html]
38
+ # plugins.
39
+ #
40
+ # == References
41
+ # * http://mongrel2.org/static/mongrel2-manual.html#x1-250003.4
42
+ #
43
+ class Config < Sequel::Model
44
+ include Mongrel2::Loggable
45
+
46
+ plugin :validation_helpers
47
+ plugin :subclasses
48
+
49
+ # Configuration defaults
50
+ DEFAULTS = {
51
+ :configdb => Mongrel2::DEFAULT_CONFIG_URI,
52
+ }
53
+
54
+ # The Pathname of the data directory
55
+ DATA_DIR = if Gem.datadir( 'mongrel2' )
56
+ Pathname( Gem.datadir('mongrel2') )
57
+ else
58
+ Pathname( __FILE__ ).dirname.parent.parent + 'data/mongrel2'
59
+ end
60
+
61
+ # The Pathname of the SQL file used to create the config database
62
+ CONFIG_SQL = DATA_DIR + 'config.sql'
63
+
64
+ # The Pathname of the SQL file used to add default mimetypes mappings to the config
65
+ # database
66
+ MIMETYPES_SQL = DATA_DIR + 'mimetypes.sql'
67
+
68
+
69
+ # Register this class as configurable if Configurability is loaded.
70
+ if defined?( Configurability )
71
+ extend Configurability
72
+ config_key :mongrel2
73
+ end
74
+
75
+
76
+ ### Return a bound Method object to the Sequel constructor of choice for the
77
+ ### config DB. This is used to choose either 'amalgalite' or 'sqlite3', preferring
78
+ ### the former.
79
+ def self::adapter_method
80
+ if defined?( ::Amalgalite )
81
+ return Sequel.method( :amalgalite )
82
+ else
83
+ return Sequel.method( :sqlite )
84
+ end
85
+ end
86
+
87
+
88
+ ### Return a Sequel::Database for an in-memory database via the available SQLite library
89
+ def self::in_memory_db
90
+ # Just calling either .amalgalite or .sqlite returns an in-memory DB
91
+ return self.adapter_method.call
92
+ end
93
+
94
+
95
+ ### Configurability API -- called when the configuration is loaded with the
96
+ ### 'mongrel2' section of the config file if there is one. This method can also be used
97
+ ### without Configurability by passing an object that can be merged with
98
+ ### Mongrel2::Config::DEFAULTS.
99
+ def self::configure( config={} )
100
+ config = DEFAULTS.merge( config )
101
+
102
+ if config[ :configdb ]
103
+ self.db = self.adapter_method.call( config[:configdb] )
104
+ end
105
+ end
106
+
107
+
108
+ ### Reset the database connection that all model objects will use to +newdb+, which should
109
+ ### be a Sequel::Database.
110
+ def self::db=( newdb )
111
+ super
112
+ if self == Mongrel2::Config
113
+ Mongrel2.log.debug "Resetting database connection for %d config classes to: %p" %
114
+ [ self.descendents.length, newdb ]
115
+ self.descendents.each {|subclass| subclass.db = newdb }
116
+ end
117
+ end
118
+
119
+
120
+ ### Return the Array of currently-configured servers in the config database as
121
+ ### Mongrel2::Config::Server objects.
122
+ def self::servers
123
+ return Mongrel2::Config::Server.all
124
+ end
125
+
126
+
127
+ ### Return the contents of the configuration schema SQL file.
128
+ def self::load_config_schema
129
+ return CONFIG_SQL.read
130
+ end
131
+
132
+
133
+ ### Return the contents of the mimetypes SQL file.
134
+ def self::load_mimetypes_sql
135
+ return MIMETYPES_SQL.read
136
+ end
137
+
138
+
139
+ ### Returns +true+ if the config database has been installed. This currently only
140
+ ### checks to see if the 'server' table exists for the sake of speed.
141
+ def self::database_initialized?
142
+ return self.db.table_exists?( :server )
143
+ end
144
+
145
+
146
+ ### Initialize the currently-configured database (if it hasn't been already)
147
+ def self::init_database
148
+ return if self.database_initialized?
149
+ return self.init_database!
150
+ end
151
+
152
+
153
+ ### Initialize the currently-configured database, dropping any existing tables.
154
+ def self::init_database!
155
+ sql = self.load_config_schema
156
+ mimetypes_sql = self.load_mimetypes_sql
157
+
158
+ Mongrel2.log.warn "Installing config schema."
159
+
160
+ self.db.execute_ddl( sql )
161
+ self.db.run( mimetypes_sql )
162
+
163
+ # Force the associations to reset
164
+ self.db = db
165
+ end
166
+
167
+
168
+ ### Return a Pathname object for the current database, or nil if the current
169
+ ### database is an in-memory one.
170
+ def self::pathname
171
+ dburi = URI( self.db.uri )
172
+ pathname = dburi.path
173
+ pathname.slice!( 0, 1 ) if pathname.start_with?( '/' )
174
+ return nil if pathname.empty?
175
+ return Pathname( pathname )
176
+ end
177
+
178
+ end # class Config
179
+
180
+
181
+ ### Factory method that creates subclasses of Mongrel2::Config.
182
+ def self::Config( source )
183
+ unless Sequel::Model::ANONYMOUS_MODEL_CLASSES.key?( source )
184
+ anonclass = nil
185
+ if source.is_a?( Sequel::Database )
186
+ anonclass = Class.new( Mongrel2::Config )
187
+ anonclass.db = source
188
+ else
189
+ anonclass = Class.new( Mongrel2::Config ).set_dataset( source )
190
+ end
191
+
192
+ Sequel::Model::ANONYMOUS_MODEL_CLASSES[ source ] = anonclass
193
+ end
194
+
195
+ return Sequel::Model::ANONYMOUS_MODEL_CLASSES[ source ]
196
+ end
197
+
198
+ require 'mongrel2/config/directory'
199
+ require 'mongrel2/config/handler'
200
+ require 'mongrel2/config/host'
201
+ require 'mongrel2/config/proxy'
202
+ require 'mongrel2/config/route'
203
+ require 'mongrel2/config/server'
204
+ require 'mongrel2/config/setting'
205
+ require 'mongrel2/config/mimetype'
206
+ require 'mongrel2/config/log'
207
+ require 'mongrel2/config/statistic'
208
+
209
+ require 'mongrel2/config/dsl'
210
+
211
+ end # module Mongrel2
212
+