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