mongrel2 0.48.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'pathname'
4
- require 'tmpdir'
5
-
6
- # The Mongrel config used by the examples. Load it with:
7
- #
8
- # m2sh.rb -c examples.sqlite load examples/config.rb
9
- #
10
-
11
- examples_dir = Pathname( __FILE__ ).dirname
12
- basedir = examples_dir.parent
13
- upload_dir = Pathname( Dir.tmpdir )
14
-
15
- # samples server
16
- s = server 'examples' do
17
-
18
- name 'Examples'
19
- default_host 'localhost'
20
-
21
- access_log '/logs/access.log'
22
- error_log '/logs/error.log'
23
- chroot basedir
24
- pid_file '/var/run/mongrel2.pid'
25
-
26
- bind_addr '0.0.0.0'
27
- port 8113
28
-
29
- xrequest '/usr/local/lib/mongrel2/filters/sendfile.so'
30
-
31
- # your main host
32
- host 'localhost' do
33
-
34
- route '/', directory( "#{basedir}/data/mongrel2/", 'bootstrap.html', 'text/html' )
35
- route '/source', directory( "#{basedir}/examples/", 'README.txt', 'text/plain' )
36
-
37
- # Handlers
38
- dumper = handler( 'tcp://127.0.0.1:9997', 'request-dumper', protocol: 'tnetstring' )
39
- route '/hello', handler( 'tcp://127.0.0.1:9999', 'helloworld-handler' )
40
- route '/async-upload', handler( 'tcp://127.0.0.1:9950', 'async-upload' )
41
- route '/sendfile', handler( 'tcp://127.0.0.1:9895', 'sendfile-quine' )
42
- route '/ws', handler( 'tcp://127.0.0.1:9995', 'ws-echo' )
43
- route '/dump', dumper
44
- route '@js', dumper
45
- route '<xml', dumper
46
-
47
- end
48
-
49
- end
50
-
51
- setting 'zeromq.threads', 1
52
- setting 'limits.content_length', 4096
53
- setting 'control_port', 'ipc://var/run/control'
54
- setting 'upload.temp_store', upload_dir + 'mongrel2.upload.XXXXXX'
55
-
56
- mkdir_p 'var/run'
57
- mkdir_p 'var/mongrel2'
58
- mkdir_p 'logs'
59
- mkdir_p '/tmp/mongrel2-uploads'
60
-
61
- puts "Will chroot to: #{s.chroot}"
62
- puts "Upload dir is: #{upload_dir}"
63
-
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'loggability'
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
- configdb = ARGV.shift || 'examples.sqlite'
24
-
25
- # Log to a file instead of STDERR for a bit more speed.
26
- # Loggability.output_to( 'hello-world.log' )
27
- Loggability.level = :debug
28
-
29
- # Point to the config database, which will cause the handler to use
30
- # its ID to look up its own socket info.
31
- Mongrel2::Config.configure( configdb: configdb )
32
- HelloWorldHandler.run( 'helloworld-handler' )
33
-
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'pathname'
4
- require 'loggability'
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
- $SAFE = 1
21
- end
22
-
23
-
24
- ### Handle a request
25
- def handle( request )
26
- template = @template.dup
27
- response = request.response
28
-
29
- template.request = request
30
- template.title = "Ruby-Mongrel2 Request Dumper"
31
- template.safelevel = $SAFE
32
-
33
- response.status = 200
34
- response.headers.content_type = 'text/html'
35
- response.puts( template )
36
-
37
- response
38
- end
39
-
40
- end # class RequestDumper
41
-
42
- Loggability.level = :debug
43
- Loggability[ Inversion ].level = :info
44
-
45
- # Point to the config database, which will cause the handler to use
46
- # its ID to look up its own socket info.
47
- Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
48
- RequestDumper.run( 'request-dumper' )
49
-
@@ -1,78 +0,0 @@
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
- <link rel="stylesheet" href="/css/master.css" type="text/css" media="screen"
13
- title="no title" charset="utf-8" />
14
- </head>
15
-
16
- <body>
17
-
18
- <header>
19
- <h1>Request Dump (0x<?call "%0x" % request.object_id ?>)</h1>
20
- </header>
21
-
22
- <section id="dump">
23
-
24
- <table>
25
- <tr>
26
- <th>Sender ID</th>
27
- <td><?escape request.sender_id ?></td>
28
- </tr>
29
- <tr>
30
- <th>Connection ID</th>
31
- <td><?escape request.conn_id.to_s ?></td>
32
- </tr>
33
- <tr>
34
- <th>$SAFE</th>
35
- <td><?attr safelevel ?></td>
36
- </tr>
37
- </table>
38
-
39
- <section id="headers">
40
- <header>
41
- <h1>Headers</h1>
42
- </header>
43
- <table>
44
- <?for name, val in request.headers.each_header.each ?>
45
- <tr>
46
- <th><?attr name ?></th>
47
- <td><?attr val ?></td>
48
- </tr>
49
- <?end for ?>
50
- </table>
51
- </section>
52
-
53
- <section id="body">
54
- <header>
55
- <h1>Body</h1>
56
- </header>
57
-
58
- <p><code><?escape request.body.inspect ?></code>
59
- (<?call "%0.2fKb" % request.body.size ?>, <?call request.body.external_encoding.name ?>)</p>
60
- </section>
61
-
62
- <section id="inspect">
63
- <header>
64
- <h1>Inspected Request</h1>
65
- <pre>
66
- <?pp request ?>
67
- </pre>
68
- </header>
69
- </section>
70
-
71
- </section>
72
-
73
- <footer>
74
- <p><tt>$Id: request-dumper.tmpl,v dae4f2b16ef7 2012/06/20 17:48:00 ged $</tt></p>
75
- </footer>
76
-
77
- </body>
78
- </html>
@@ -1,22 +0,0 @@
1
- #!/bin/sh
2
-
3
- if [ "$(type foreman 2>/dev/null)" == "" ]; then
4
- echo "You need Foreman installed to start the examples (gem install foreman)"
5
- exit 1
6
- fi
7
-
8
- examplesdir=$(dirname $0)
9
-
10
- echo "Creating/updating the config database..."
11
- /usr/bin/env ruby -rubygems -I${examplesdir}/../lib $examplesdir/../bin/m2sh.rb \
12
- -c ${examplesdir}/examples.sqlite load $examplesdir/config.rb
13
-
14
- if [ $? != 0 ]; then
15
- echo "Oops, problems installing the config DB. Aborting."
16
- exit 1
17
- fi
18
-
19
- echo "Okay, now point a browser to http://localhost:8113/."
20
- cd $examplesdir
21
- foreman start
22
-
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'pathname'
4
- require 'loggability'
5
- require 'mongrel2/config'
6
- require 'mongrel2/handler'
7
-
8
- # A handler that sends itself in a 'sendfile' extended response.
9
- class SendfileQuine < Mongrel2::Handler
10
-
11
- ### Get the fully-qualified path to the handler on startup
12
- def initialize( * )
13
- super
14
- @path = Pathname( __FILE__ ).expand_path
15
- @size = @path.size
16
- end
17
-
18
-
19
- ### Handle a request
20
- def handle( request )
21
- response = request.response
22
-
23
- response.headers.content_type = 'text/plain'
24
- response.headers.content_length = @size
25
- response.extend_reply_with( :sendfile )
26
- response.extended_reply_data << @path.to_s
27
-
28
- response
29
- end
30
-
31
- end # class RequestDumper
32
-
33
- Loggability.level = :debug
34
-
35
- # Point to the config database, which will cause the handler to use
36
- # its ID to look up its own socket info.
37
- Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
38
- SendfileQuine.run( 'sendfile-quine' )
39
-
@@ -1,263 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- require 'loggability'
5
- require 'pathname'
6
- require 'mongrel2/config'
7
- require 'mongrel2/handler'
8
-
9
-
10
- # An example of a WebSocket (RFC6455) Mongrel2 application that echoes back whatever
11
- # (non-control) frames you send it. It will also ping all of its clients and disconnect
12
- # ones that don't reply within a given period.
13
- class WebSocketEchoServer < Mongrel2::Handler
14
- include Mongrel2::WebSocket::Constants
15
-
16
- # Number of seconds to wait between heartbeat PING frames
17
- HEARTBEAT_RATE = 5.0
18
-
19
- # Number of seconds to wait for a data frame or a PONG before considering
20
- # a socket idle
21
- SOCKET_IDLE_TIMEOUT = 15.0
22
-
23
-
24
- # Add an instance variable to keep track of connections, and one for the
25
- # heartbeat thread.
26
- def initialize( * )
27
- super
28
- @connections = {}
29
- @heartbeat = nil
30
- end
31
-
32
-
33
- ######
34
- public
35
- ######
36
-
37
- # A Hash of last seen times keyed by [sender ID, connection ID] tuple
38
- attr_reader :connections
39
-
40
- # The Thread that sends PING frames to connected sockets and cull any
41
- # that don't reply within SOCKET_IDLE_TIMEOUT seconds
42
- attr_reader :heartbeat
43
-
44
-
45
- # Called by Mongrel2::Handler when it starts accepting requests. Overridden
46
- # to start up the heartbeat thread.
47
- def start_accepting_requests
48
- self.start_heartbeat
49
- super
50
- end
51
-
52
-
53
- # Called by Mongrel2::Handler when the server is restarted. Overridden to
54
- # restart the heartbeat thread.
55
- def restart
56
- self.stop_heartbeat
57
- super
58
- self.start_heartbeat
59
- end
60
-
61
-
62
- # Called by Mongrel2::Handler when the server is shut down. Overridden to
63
- # stop the heartbeat thread.
64
- def shutdown
65
- self.stop_heartbeat
66
- super
67
- end
68
-
69
-
70
- # Mongrel2 will send a disconnect notice when a client's connection closes;
71
- # delete the connection when it does.
72
- def handle_disconnect( request )
73
- self.log.info "Client %d: disconnect." % [ request.conn_id ]
74
- self.connections.delete( [request.sender_id, request.conn_id] )
75
- return nil
76
- end
77
-
78
-
79
- # Non-websocket (e.g., plain HTTP) requests would ordinarily just get
80
- # a 204 NO CONTENT response, but we tell Mongrel2 to just drop such connections
81
- # immediately.
82
- def handle( request )
83
- self.log.info "Regular HTTP request (%s): closing channel." % [ request ]
84
- self.conn.reply_close( request )
85
- return nil
86
- end
87
-
88
-
89
- # Handle the initial handshake. Assumes no sub-protocols or protocol version
90
- # checks are necessary.
91
- def handle_websocket_handshake( handshake )
92
- self.log.info "Handshake from %s" % [ handshake.remote_ip ]
93
-
94
- response = handshake.response( handshake.protocols.first )
95
- @connections[ [handshake.sender_id, handshake.conn_id] ] = Time.now
96
-
97
- return response
98
- end
99
-
100
-
101
- # This is the main handler for WebSocket requests. Each frame comes in as a
102
- # Mongrel::WebSocket::Frame object, and then is dispatched according to what
103
- # opcode it has.
104
- def handle_websocket( frame )
105
-
106
- # Log each frame
107
- self.log.info "%s/%d: %s%s [%s]: %p" % [
108
- frame.sender_id,
109
- frame.conn_id,
110
- frame.opcode.to_s.upcase,
111
- frame.fin? ? '' : '(cont)',
112
- frame.headers.x_forwarded_for,
113
- frame.payload.read( 20 ),
114
- ]
115
- frame.payload.rewind
116
-
117
- # If a client sends an invalid frame, close their connection, but politely.
118
- if !frame.valid?
119
- self.log.error " invalid frame from client: %s" % [ frame.errors.join(';') ]
120
- res = frame.response( :close )
121
- res.set_status( CLOSE_PROTOCOL_ERROR )
122
- return res
123
- end
124
-
125
- # Update the 'last-seen' time unless the connection is closing
126
- unless frame.opcode == :close
127
- @connections[ [frame.sender_id, frame.conn_id] ] = Time.now
128
- end
129
-
130
- # Use the opcode to decide what method to call
131
- self.log.debug "Handling a %s frame." % [ frame.opcode ]
132
- handler = self.method( "handle_%s_frame" % [frame.opcode] )
133
- return handler.call( frame )
134
- end
135
-
136
-
137
- # Handle TEXT, BINARY, and CONTINUATION frames by replying with an echo of the
138
- # same data. Fragmented frames get echoed back as-is without any reassembly.
139
- def handle_text_frame( frame )
140
- self.log.info "Echoing data frame: %p" % [ frame ]
141
-
142
- # Make the response frame
143
- response = frame.response
144
- response.fin = frame.fin?
145
- IO.copy_stream( frame.payload, response.payload )
146
-
147
- return response
148
- end
149
- alias_method :handle_binary_frame, :handle_text_frame
150
- alias_method :handle_continuation_frame, :handle_text_frame
151
-
152
-
153
- # Handle close frames
154
- def handle_close_frame( frame )
155
-
156
- # There will still be a connection slot if this close originated with
157
- # the client. In that case, reply with the ACK CLOSE frame
158
- self.conn.reply( frame.response(:close) ) if
159
- self.connections.delete( [frame.sender_id, frame.conn_id] )
160
-
161
- self.conn.reply_close( frame )
162
- return nil
163
- end
164
-
165
-
166
- # Handle a PING frame; the response is a PONG with the same payload.
167
- def handle_ping_frame( frame )
168
- return frame.response
169
- end
170
-
171
-
172
- # Handle a PONG frame; nothing really to do
173
- def handle_pong_frame( frame )
174
- return nil
175
- end
176
-
177
-
178
- # Start a thread that will periodically ping connected sockets and remove any
179
- # connections that don't reply
180
- def start_heartbeat
181
- self.log.info "Starting heartbeat thread."
182
- @heartbeat = Thread.new do
183
- Thread.current.abort_on_exception = true
184
- self.log.debug " Heartbeat thread started: %p" % [ Thread.current ]
185
-
186
- # Use a thread-local variable to signal the thread to shut down
187
- Thread.current[ :shutdown ] = false
188
- until Thread.current[ :shutdown ]
189
-
190
- # If there are active connections, remove any that have
191
- # timed out and ping the rest
192
- unless self.connections.empty?
193
- self.cull_idle_sockets
194
- self.ping_all_sockets
195
- end
196
-
197
- self.log.debug " heartbeat thread sleeping"
198
- sleep( HEARTBEAT_RATE )
199
- self.log.debug " heartbeat thread waking up"
200
- end
201
-
202
- self.log.info "Hearbeat thread exiting."
203
- end
204
- end
205
-
206
-
207
- # Tell the heartbeat thread to exit.
208
- def stop_heartbeat
209
- @heartbeat[ :shutdown ] = true
210
- @heartbeat.run.join if @heartbeat.alive?
211
- end
212
-
213
-
214
- # Disconnect any sockets that haven't sent any frames for at least
215
- # SOCKET_IDLE_TIMEOUT seconds.
216
- def cull_idle_sockets
217
- self.log.debug "Culling idle sockets."
218
-
219
- earliest = Time.now - SOCKET_IDLE_TIMEOUT
220
-
221
- self.connections.each do |(sender_id, conn_id), lastframe|
222
- next unless earliest > lastframe
223
-
224
- # Make a CLOSE frame
225
- frame = Mongrel2::WebSocket::Frame.new( sender_id, conn_id, '', {}, '' )
226
- frame.opcode = :close
227
- frame.set_status( CLOSE_EXCEPTION )
228
-
229
- # Use the connection directly so we can send a frame and close the
230
- # connection
231
- self.conn.reply( frame )
232
- self.conn.send_close( sender_id, conn_id )
233
- end
234
- end
235
-
236
-
237
- # Send a PING frame to all connected sockets.
238
- def ping_all_sockets
239
- self.log.debug "Pinging all connected sockets."
240
-
241
- self.connections.each do |(sender_id, conn_id), hash|
242
- frame = Mongrel2::WebSocket::Frame.new( sender_id, conn_id, '', {}, 'heartbeat' )
243
- frame.opcode = :ping
244
- frame.fin = true
245
-
246
- self.log.debug " %s/%d: PING" % [ sender_id, conn_id ]
247
- self.conn.reply( frame )
248
- end
249
-
250
- self.log.debug " done with pings."
251
- end
252
-
253
-
254
- end # class RequestDumper
255
-
256
- Loggability.level = $DEBUG||$VERBOSE ? :debug : :info
257
- Loggability.format_as( :color ) if $stdin.tty?
258
-
259
- # Point to the config database, which will cause the handler to use
260
- # its ID to look up its own socket info.
261
- Mongrel2::Config.configure( :configdb => 'examples.sqlite' )
262
- WebSocketEchoServer.run( 'ws-echo' )
263
-