mongrel2 0.48.0 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-