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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +10 -2
- data/History.rdoc +7 -0
- data/Manifest.txt +0 -16
- data/README.rdoc +0 -4
- data/Rakefile +7 -7
- data/lib/mongrel2.rb +3 -28
- data/lib/mongrel2/connection.rb +9 -11
- data/lib/mongrel2/control.rb +6 -7
- data/lib/mongrel2/handler.rb +50 -18
- data/spec/mongrel2/connection_spec.rb +28 -50
- data/spec/mongrel2/control_spec.rb +49 -48
- data/spec/mongrel2/handler_spec.rb +52 -31
- metadata +54 -66
- metadata.gz.sig +4 -3
- data/data/mongrel2/bootstrap.html +0 -33
- data/data/mongrel2/config.rb.in +0 -71
- data/data/mongrel2/css/master.css +0 -77
- data/data/mongrel2/index.html.in +0 -51
- data/data/mongrel2/js/websock-test.js +0 -108
- data/data/mongrel2/websock-test.html +0 -33
- data/examples/Procfile +0 -7
- data/examples/README.txt +0 -6
- data/examples/async-upload.rb +0 -109
- data/examples/config.rb +0 -63
- data/examples/helloworld-handler.rb +0 -33
- data/examples/request-dumper.rb +0 -49
- data/examples/request-dumper.tmpl +0 -78
- data/examples/run +0 -22
- data/examples/sendfile.rb +0 -39
- data/examples/ws-echo.rb +0 -263
data/examples/config.rb
DELETED
@@ -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
|
-
|
data/examples/request-dumper.rb
DELETED
@@ -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>
|
data/examples/run
DELETED
@@ -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
|
-
|
data/examples/sendfile.rb
DELETED
@@ -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
|
-
|
data/examples/ws-echo.rb
DELETED
@@ -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
|
-
|