mongrel2 0.26.0 → 0.27.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +36 -1
- data/History.rdoc +6 -0
- data/Manifest.txt +1 -0
- data/README.rdoc +52 -21
- data/bin/m2sh.rb +55 -1
- data/data/mongrel2/config.rb.in +71 -0
- data/examples/.env +1 -0
- data/examples/Procfile +1 -1
- data/examples/config.rb +1 -1
- data/examples/ws-echo.rb +12 -0
- data/lib/mongrel2.rb +2 -2
- data/lib/mongrel2/config.rb +3 -8
- data/lib/mongrel2/connection.rb +1 -1
- data/lib/mongrel2/constants.rb +12 -0
- data/lib/mongrel2/control.rb +1 -1
- data/lib/mongrel2/handler.rb +21 -8
- data/lib/mongrel2/httpresponse.rb +6 -5
- data/lib/mongrel2/testing.rb +52 -8
- data/lib/mongrel2/websocket.rb +101 -5
- data/spec/lib/constants.rb +48 -0
- data/spec/lib/helpers.rb +50 -4
- data/spec/mongrel2/handler_spec.rb +36 -0
- data/spec/mongrel2/httpresponse_spec.rb +4 -3
- data/spec/mongrel2/websocket_spec.rb +64 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/ChangeLog
CHANGED
@@ -1,5 +1,40 @@
|
|
1
|
+
2012-06-30 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* Manifest.txt, README.rdoc, bin/m2sh.rb, data/mongrel2/config.rb.in,
|
4
|
+
lib/mongrel2/config.rb, lib/mongrel2/constants.rb:
|
5
|
+
Adding 'bootstrap' and 'quickstart' commands to m2sh.rb.
|
6
|
+
|
7
|
+
Also adding documentation for them to the README, and moved the
|
8
|
+
DATA_DIR constants into mongrel2/constants.
|
9
|
+
[2f2b9b4bc86f] [tip]
|
10
|
+
|
11
|
+
2012-06-27 Michael Granger <ged@FaerieMUD.org>
|
12
|
+
|
13
|
+
* examples/.env, examples/Procfile, examples/config.rb, examples/ws-
|
14
|
+
echo.rb, lib/mongrel2/handler.rb, lib/mongrel2/httpresponse.rb,
|
15
|
+
lib/mongrel2/testing.rb, lib/mongrel2/websocket.rb,
|
16
|
+
spec/lib/constants.rb, spec/lib/helpers.rb,
|
17
|
+
spec/mongrel2/handler_spec.rb, spec/mongrel2/httpresponse_spec.rb,
|
18
|
+
spec/mongrel2/websocket_spec.rb:
|
19
|
+
Updating websocket support for recent Mongrel2 changes
|
20
|
+
[c17bbae10985]
|
21
|
+
|
1
22
|
2012-06-26 Michael Granger <ged@FaerieMUD.org>
|
2
23
|
|
24
|
+
* .hgtags:
|
25
|
+
Added tag v0.26.0 for changeset 7fb25e6e09bc
|
26
|
+
[1cf11a8e5402]
|
27
|
+
|
28
|
+
* .hgsigs:
|
29
|
+
Added signature for changeset c2eac469ca66
|
30
|
+
[7fb25e6e09bc] [v0.26.0]
|
31
|
+
|
32
|
+
* History.rdoc, lib/mongrel2.rb, spec/lib/helpers.rb,
|
33
|
+
spec/mongrel2/handler_spec.rb, spec/mongrel2/request_spec.rb:
|
34
|
+
Fix the specs broken by the async upload changes, bump minor
|
35
|
+
version, update history.
|
36
|
+
[c2eac469ca66]
|
37
|
+
|
3
38
|
* lib/mongrel2/config/handler.rb, lib/mongrel2/config/host.rb,
|
4
39
|
lib/mongrel2/config/route.rb, lib/mongrel2/config/server.rb,
|
5
40
|
lib/mongrel2/connection.rb, lib/mongrel2/handler.rb,
|
@@ -7,7 +42,7 @@
|
|
7
42
|
spec/mongrel2/handler_spec.rb, spec/mongrel2/httprequest_spec.rb,
|
8
43
|
spec/mongrel2/request_spec.rb:
|
9
44
|
Fix the async upload body path
|
10
|
-
[782174dcba2e]
|
45
|
+
[782174dcba2e]
|
11
46
|
|
12
47
|
2012-06-21 Michael Granger <ged@FaerieMUD.org>
|
13
48
|
|
data/History.rdoc
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== v0.27.0 [2012-07-02] Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
- Adds support for websocket handshake in 'develop' branch.
|
4
|
+
- Adds 'bootstrap' and 'quickstart' commands to m2sh.rb.
|
5
|
+
|
6
|
+
|
1
7
|
== v0.26.0 [2012-06-26] Michael Granger <ged@FaerieMUD.org>
|
2
8
|
|
3
9
|
- Fix the derived path to the async upload body
|
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -17,29 +17,36 @@ databases in pure Ruby, a Control port interface object, and handler classes
|
|
17
17
|
for creating applications or higher-level frameworks.
|
18
18
|
|
19
19
|
|
20
|
-
== Installation
|
20
|
+
== Installation and Setup
|
21
21
|
|
22
|
-
|
22
|
+
Install mongrel2:
|
23
23
|
|
24
|
+
$ {brew,port,portmaster,apt-get install,etc} mongrel2
|
24
25
|
|
25
|
-
|
26
|
-
classes, but it will also fall back to using the sqlite3 library instead:
|
26
|
+
Install the mongrel2 gem:
|
27
27
|
|
28
|
-
|
29
|
-
$ rspec -rsqlite3 -cfp spec
|
30
|
-
>>> Using SQLite3 1.3.4 for DB access.
|
31
|
-
.....[...]
|
28
|
+
$ gem install mongrel2
|
32
29
|
|
33
|
-
|
34
|
-
102 examples, 0 failures
|
30
|
+
Dump a config database generation script into the current working directory:
|
35
31
|
|
36
|
-
|
37
|
-
$ rspec -cfp spec
|
38
|
-
>>> Using Amalgalite 1.1.2 for DB access.
|
39
|
-
.....[...]
|
32
|
+
$ m2sh.rb bootstrap
|
40
33
|
|
41
|
-
|
42
|
-
|
34
|
+
Edit the generated file:
|
35
|
+
|
36
|
+
$ $EDITOR config.rb
|
37
|
+
|
38
|
+
Create a config database from the Ruby config:
|
39
|
+
|
40
|
+
$ m2sh.rb load config.rb
|
41
|
+
|
42
|
+
Start the server:
|
43
|
+
|
44
|
+
$ m2sh.rb start
|
45
|
+
|
46
|
+
Or combine <tt>bootstrap</tt>, <tt>load</tt>, and <tt>start</tt> all into one
|
47
|
+
command:
|
48
|
+
|
49
|
+
$ m2sh.rb quickstart
|
43
50
|
|
44
51
|
|
45
52
|
== Usage
|
@@ -54,6 +61,26 @@ for adding the Ruby configuration DSL to your namespace, and the top-level
|
|
54
61
|
Mongrel2::Config class, which manages the database connection, installs the
|
55
62
|
schema, etc.
|
56
63
|
|
64
|
+
The ORM classes use Jeremy Hinegardner's 'amalgalite' library, but it will
|
65
|
+
also fall back to using the sqlite3 library instead:
|
66
|
+
|
67
|
+
# Loading the sqlite3 library explicitly
|
68
|
+
$ rspec -rsqlite3 -cfp spec
|
69
|
+
>>> Using SQLite3 1.3.4 for DB access.
|
70
|
+
.....[...]
|
71
|
+
|
72
|
+
Finished in 5.53 seconds
|
73
|
+
102 examples, 0 failures
|
74
|
+
|
75
|
+
# No -rsqlite3 means amalgalite loads first.
|
76
|
+
$ rspec -cfp spec
|
77
|
+
>>> Using Amalgalite 1.1.2 for DB access.
|
78
|
+
.....[...]
|
79
|
+
|
80
|
+
Finished in 3.67 seconds
|
81
|
+
102 examples, 0 failures
|
82
|
+
|
83
|
+
|
57
84
|
* Mongrel2::Config
|
58
85
|
* Mongrel2::Config::DSL
|
59
86
|
* Mongrel2::Config::Server
|
@@ -82,6 +109,7 @@ Mongrel2 sends:
|
|
82
109
|
* Mongrel2::HTTPRequest
|
83
110
|
* Mongrel2::JSONRequest
|
84
111
|
* Mongrel2::XMLRequest
|
112
|
+
* Mongrel2::WebSocket::ClientHandshake
|
85
113
|
* Mongrel2::WebSocket::Frame
|
86
114
|
|
87
115
|
These are all {overridable}[rdoc-ref:Mongrel2::Request.register_request_type]
|
@@ -94,17 +122,20 @@ handlers.
|
|
94
122
|
=== The Control Class
|
95
123
|
|
96
124
|
The Mongrel2::Control class is an object interface to {the Mongrel2 control
|
97
|
-
port}[http://mongrel2.org/static/
|
125
|
+
port}[http://mongrel2.org/static/book-finalch4.html#x6-390003.8]. It can be
|
98
126
|
used to stop and restart the server, check its status, etc.
|
99
127
|
|
100
128
|
|
101
129
|
=== Other Classes
|
102
130
|
|
103
|
-
There are a few other classes and modules
|
131
|
+
There are a few other classes and modules worth checking out, too:
|
104
132
|
|
105
|
-
|
106
|
-
|
107
|
-
|
133
|
+
Mongrel2::Table::
|
134
|
+
A hash-like data structure for headers, etc.
|
135
|
+
Mongrel2::Constants::
|
136
|
+
A collection of convenience constants for Mongrel2 handlers.
|
137
|
+
Mongrel2::RequestFactory::
|
138
|
+
A factory for generating fixtured requests of various types for testing.
|
108
139
|
|
109
140
|
|
110
141
|
== Contributing
|
data/bin/m2sh.rb
CHANGED
@@ -186,7 +186,7 @@ class Mongrel2::M2SHCommand
|
|
186
186
|
text ''
|
187
187
|
|
188
188
|
text 'Global Options'
|
189
|
-
opt :config, "Specify the
|
189
|
+
opt :config, "Specify the config database to use.",
|
190
190
|
:default => DEFAULT_CONFIG_URI
|
191
191
|
opt :sudo, "Use 'sudo' to run the mongrel2 server."
|
192
192
|
opt :port, "Reset the server port to <i> before starting it.",
|
@@ -588,6 +588,43 @@ class Mongrel2::M2SHCommand
|
|
588
588
|
usage :running, "[server]"
|
589
589
|
|
590
590
|
|
591
|
+
### The 'bootstrap' command.
|
592
|
+
def bootstrap_command( *args )
|
593
|
+
scriptname = args.shift || DEFAULT_CONFIG_SCRIPT
|
594
|
+
template = Mongrel2::DATA_DIR + 'config.rb.in'
|
595
|
+
data = template.read
|
596
|
+
|
597
|
+
data.gsub!( /%% PWD %%/, Dir.pwd )
|
598
|
+
|
599
|
+
header "Writing a config-generation script to %s" % [ scriptname ]
|
600
|
+
File.open( scriptname, File::WRONLY|File::EXCL|File::CREAT, 0755, encoding: 'utf-8' ) do |fh|
|
601
|
+
fh.print( data )
|
602
|
+
end
|
603
|
+
message "Done."
|
604
|
+
end
|
605
|
+
help :bootstrap, "Generate a basic config-generation script."
|
606
|
+
usage :boostrap, "[scriptname]"
|
607
|
+
|
608
|
+
|
609
|
+
### The 'quickstart' command.
|
610
|
+
def quickstart_command( *args )
|
611
|
+
configfile = 'config.rb'
|
612
|
+
|
613
|
+
header "Quickstart!"
|
614
|
+
self.bootstrap_command( configfile )
|
615
|
+
edit( configfile )
|
616
|
+
self.load_command( configfile )
|
617
|
+
|
618
|
+
message '---'
|
619
|
+
header "Point a browser at: "
|
620
|
+
message '---'
|
621
|
+
|
622
|
+
self.start_command()
|
623
|
+
end
|
624
|
+
help :quickstart, "Set up a basic mongrel2 server and run it."
|
625
|
+
usage :quickstart
|
626
|
+
|
627
|
+
|
591
628
|
### The 'version' command
|
592
629
|
def version_command( *args )
|
593
630
|
message( "<%= color 'Version:', :header %> " + Mongrel2.version_string(true) )
|
@@ -599,6 +636,10 @@ class Mongrel2::M2SHCommand
|
|
599
636
|
# Utility methods
|
600
637
|
#
|
601
638
|
|
639
|
+
#######
|
640
|
+
private
|
641
|
+
#######
|
642
|
+
|
602
643
|
### Output normal output
|
603
644
|
def message( *parts )
|
604
645
|
self.prompt.say( parts.map(&:to_s).join($/) )
|
@@ -676,6 +717,19 @@ class Mongrel2::M2SHCommand
|
|
676
717
|
return server
|
677
718
|
end
|
678
719
|
|
720
|
+
|
721
|
+
### Invoke the user's editor on the given +filename+ and return the exit code
|
722
|
+
### from doing so.
|
723
|
+
def edit( filename )
|
724
|
+
editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
|
725
|
+
system editor, filename.to_s
|
726
|
+
unless $?.success? || editor =~ /vim/i
|
727
|
+
raise "Editor exited with an error status (%d)" % [ $?.exitstatus ]
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
|
732
|
+
|
679
733
|
end # class Mongrel2::M2SHCommand
|
680
734
|
|
681
735
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
# This is a Ruby script that will *generate* the SQLite database
|
8
|
+
# that Mongrel2 uses for its configuration. You can just as easily
|
9
|
+
# use Mongrel2's 'm2sh' and the Pythonish config syntax described
|
10
|
+
# in the manual if you prefer that.
|
11
|
+
#
|
12
|
+
# See the "Mongrel2 Config DSL" section of the API docs, and the "How A
|
13
|
+
# Config Is Structured" section of the manual for details
|
14
|
+
# on specific items:
|
15
|
+
#
|
16
|
+
# Mongrel2 Config DSL::
|
17
|
+
# http://deveiate.org/code/mongrel2/DSL_rdoc.html
|
18
|
+
#
|
19
|
+
# How A Config Is Structured::
|
20
|
+
# http://mongrel2.org/static/book-finalch4.html#x6-260003.4
|
21
|
+
#
|
22
|
+
# You can load this via the 'm2sh.rb' tool that comes with the 'mongrel2'
|
23
|
+
# gem:
|
24
|
+
#
|
25
|
+
# m2sh.rb -c config.sqlite load config.rb
|
26
|
+
|
27
|
+
# Establish some directories
|
28
|
+
base_dir = Pathname( '%% PWD %%' )
|
29
|
+
upload_dir = Pathname( Dir.tmpdir ) + 'm2spool'
|
30
|
+
|
31
|
+
# Main Mongrel2 server config
|
32
|
+
main = server 'main' do
|
33
|
+
|
34
|
+
name 'Main'
|
35
|
+
default_host 'localhost'
|
36
|
+
chroot base_dir
|
37
|
+
|
38
|
+
# All of these values are relative to the 'chroot' value if Mongrel2
|
39
|
+
# is started as root. If it's not, they're relative to the directory
|
40
|
+
# it's started in.
|
41
|
+
access_log '/logs/access.log'
|
42
|
+
error_log '/logs/error.log'
|
43
|
+
pid_file '/var/run/mongrel2.pid'
|
44
|
+
|
45
|
+
# This the address and port the server will listen on. You can
|
46
|
+
# use '0.0.0.0' as the bind_addr to listen on all interfaces/IPs.
|
47
|
+
bind_addr '127.0.0.1'
|
48
|
+
port 8113
|
49
|
+
|
50
|
+
host 'localhost' do
|
51
|
+
|
52
|
+
# Serve static content out of a 'public' subdirectory
|
53
|
+
route '/', directory( "public/", 'index.html', 'text/html' )
|
54
|
+
|
55
|
+
# Dynamic content is served via handler routes
|
56
|
+
route '/hello', handler( 'tcp://127.0.0.1:9999', 'helloworld' )
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
setting 'limits.content_length', 512 * 1024
|
63
|
+
setting 'control_port', 'ipc://var/run/mongrel2.sock'
|
64
|
+
setting 'upload.temp_store', upload_dir + 'mongrel2.upload.XXXXXX'
|
65
|
+
|
66
|
+
# Make relative directories so that starting as a regular user works
|
67
|
+
(base_dir + "./#{main.access_log}").dirname.mkpath
|
68
|
+
(base_dir + "./#{main.error_log}").dirname.mkpath
|
69
|
+
(base_dir + "./#{main.pid_file}").dirname.mkpath
|
70
|
+
mkdir_p( upload_dir )
|
71
|
+
|
data/examples/.env
CHANGED
data/examples/Procfile
CHANGED
data/examples/config.rb
CHANGED
data/examples/ws-echo.rb
CHANGED
@@ -86,6 +86,18 @@ class WebSocketEchoServer < Mongrel2::Handler
|
|
86
86
|
end
|
87
87
|
|
88
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
|
+
|
89
101
|
# This is the main handler for WebSocket requests. Each frame comes in as a
|
90
102
|
# Mongrel::WebSocket::Frame object, and then is dispatched according to what
|
91
103
|
# opcode it has.
|
data/lib/mongrel2.rb
CHANGED
@@ -20,10 +20,10 @@ module Mongrel2
|
|
20
20
|
abort "\n\n>>> Mongrel2 requires Ruby 1.9.2 or later. <<<\n\n" if RUBY_VERSION < '1.9.2'
|
21
21
|
|
22
22
|
# Library version constant
|
23
|
-
VERSION = '0.
|
23
|
+
VERSION = '0.27.0'
|
24
24
|
|
25
25
|
# Version-control revision constant
|
26
|
-
REVISION = %q$Revision:
|
26
|
+
REVISION = %q$Revision: 6de3cbe2409c $
|
27
27
|
|
28
28
|
|
29
29
|
require 'mongrel2/constants'
|
data/lib/mongrel2/config.rb
CHANGED
@@ -28,6 +28,7 @@ end
|
|
28
28
|
|
29
29
|
require 'mongrel2' unless defined?( Mongrel2 )
|
30
30
|
require 'mongrel2/table'
|
31
|
+
require 'mongrel2/constants'
|
31
32
|
|
32
33
|
module Mongrel2
|
33
34
|
|
@@ -42,10 +43,11 @@ module Mongrel2
|
|
42
43
|
# plugins.
|
43
44
|
#
|
44
45
|
# == References
|
45
|
-
# * http://mongrel2.org/static/
|
46
|
+
# * http://mongrel2.org/static/book-finalch4.html#x6-260003.4
|
46
47
|
#
|
47
48
|
class Config < Sequel::Model
|
48
49
|
extend Loggability
|
50
|
+
include Mongrel2::Constants
|
49
51
|
|
50
52
|
# Loggability API -- set up logging under the 'mongrel2' log host
|
51
53
|
log_to :mongrel2
|
@@ -63,13 +65,6 @@ module Mongrel2
|
|
63
65
|
}
|
64
66
|
DEFAULTS = CONFIG_DEFAULTS
|
65
67
|
|
66
|
-
# The Pathname of the data directory
|
67
|
-
DATA_DIR = if Gem.datadir( 'mongrel2' )
|
68
|
-
Pathname( Gem.datadir('mongrel2') )
|
69
|
-
else
|
70
|
-
Pathname( __FILE__ ).dirname.parent.parent + 'data/mongrel2'
|
71
|
-
end
|
72
|
-
|
73
68
|
# The Pathname of the SQL file used to create the config database
|
74
69
|
CONFIG_SQL = DATA_DIR + 'config.sql'
|
75
70
|
|
data/lib/mongrel2/connection.rb
CHANGED
@@ -15,7 +15,7 @@ require 'mongrel2' unless defined?( Mongrel2 )
|
|
15
15
|
# then encodes and sends Mongrel2::Response objects back to the server.
|
16
16
|
#
|
17
17
|
# == References
|
18
|
-
# * http://mongrel2.org/static/
|
18
|
+
# * http://mongrel2.org/static/book-finalch6.html#x8-710005.3
|
19
19
|
class Mongrel2::Connection
|
20
20
|
extend Loggability
|
21
21
|
|
data/lib/mongrel2/constants.rb
CHANGED
@@ -8,9 +8,21 @@ require 'mongrel2' unless defined?( Mongrel2 )
|
|
8
8
|
# A collection of constants that are shared across the library
|
9
9
|
module Mongrel2::Constants
|
10
10
|
|
11
|
+
# The Pathname of the data directory
|
12
|
+
DATA_DIR = if ENV['MONGREL2_DATADIR']
|
13
|
+
Pathname( ENV['MONGREL2_DATADIR'] )
|
14
|
+
elsif Gem.datadir( 'mongrel2' )
|
15
|
+
Pathname( Gem.datadir('mongrel2') )
|
16
|
+
else
|
17
|
+
Pathname( __FILE__ ).dirname.parent.parent + 'data/mongrel2'
|
18
|
+
end
|
19
|
+
|
11
20
|
# The path to the default Sqlite configuration database
|
12
21
|
DEFAULT_CONFIG_URI = 'config.sqlite'
|
13
22
|
|
23
|
+
# The default name of the config-generation script
|
24
|
+
DEFAULT_CONFIG_SCRIPT = 'config.rb'
|
25
|
+
|
14
26
|
# The default URI of the control socket
|
15
27
|
DEFAULT_CONTROL_SOCKET = 'ipc://run/control'
|
16
28
|
|
data/lib/mongrel2/control.rb
CHANGED
@@ -11,7 +11,7 @@ require 'mongrel2' unless defined?( Mongrel2 )
|
|
11
11
|
# An interface to the Mongrel2 control port.
|
12
12
|
#
|
13
13
|
# == References
|
14
|
-
# (http://mongrel2.org/static/
|
14
|
+
# (http://mongrel2.org/static/book-finalch4.html#x6-390003.8)
|
15
15
|
class Mongrel2::Control
|
16
16
|
extend Loggability
|
17
17
|
|
data/lib/mongrel2/handler.rb
CHANGED
@@ -207,19 +207,18 @@ class Mongrel2::Handler
|
|
207
207
|
return self.handle_async_upload_start( request )
|
208
208
|
|
209
209
|
else
|
210
|
+
self.log.debug "%s request." % [ request.headers['METHOD'] ]
|
210
211
|
case request
|
212
|
+
when Mongrel2::WebSocket::ClientHandshake
|
213
|
+
return self.handle_websocket_handshake( request )
|
214
|
+
when Mongrel2::WebSocket::Frame
|
215
|
+
return self.handle_websocket( request )
|
211
216
|
when Mongrel2::HTTPRequest
|
212
|
-
self.log.debug "HTTP request."
|
213
217
|
return self.handle( request )
|
214
218
|
when Mongrel2::JSONRequest
|
215
|
-
self.log.debug "JSON message request."
|
216
219
|
return self.handle_json( request )
|
217
220
|
when Mongrel2::XMLRequest
|
218
|
-
self.log.debug "XML message request."
|
219
221
|
return self.handle_xml( request )
|
220
|
-
when Mongrel2::WebSocket::Frame
|
221
|
-
self.log.debug "WEBSOCKET message request."
|
222
|
-
return self.handle_websocket( request )
|
223
222
|
else
|
224
223
|
self.log.error "Unhandled request type %s (%p)" %
|
225
224
|
[ request.headers['METHOD'], request.class ]
|
@@ -279,10 +278,24 @@ class Mongrel2::Handler
|
|
279
278
|
### Handle a WebSocket frame in +request+. If not overridden, WebSocket connections are
|
280
279
|
### closed with a policy error status.
|
281
280
|
def handle_websocket( request )
|
282
|
-
self.log.warn "Unhandled WEBSOCKET
|
281
|
+
self.log.warn "Unhandled WEBSOCKET frame (%p)" % [ request.headers.path ]
|
283
282
|
res = request.response
|
284
283
|
res.make_close_frame( WebSocket::CLOSE_POLICY_VIOLATION )
|
285
|
-
|
284
|
+
self.conn.reply( res)
|
285
|
+
|
286
|
+
self.conn.reply_close( request )
|
287
|
+
|
288
|
+
return nil
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
### Handle a WebSocket handshake HTTP +request+. If not overridden, this method drops
|
293
|
+
### the connection.
|
294
|
+
def handle_websocket_handshake( handshake )
|
295
|
+
self.log.warn "Unhandled WEBSOCKET_HANDSHAKE request (%p)" % [ request.headers.path ]
|
296
|
+
self.conn.reply_close( request )
|
297
|
+
|
298
|
+
return nil
|
286
299
|
end
|
287
300
|
|
288
301
|
|
@@ -169,12 +169,13 @@ class Mongrel2::HTTPResponse < Mongrel2::Response
|
|
169
169
|
headers = self.headers.dup
|
170
170
|
|
171
171
|
headers[:date] ||= Time.now.httpdate
|
172
|
-
headers[:content_length] ||= self.get_content_length
|
173
172
|
|
174
173
|
if self.bodiless?
|
174
|
+
headers.delete( :content_length )
|
175
175
|
headers.delete( :content_type )
|
176
176
|
else
|
177
|
-
headers[:
|
177
|
+
headers[:content_length] ||= self.get_content_length
|
178
|
+
headers[:content_type] ||= DEFAULT_CONTENT_TYPE.dup
|
178
179
|
end
|
179
180
|
|
180
181
|
return headers
|
@@ -184,11 +185,11 @@ class Mongrel2::HTTPResponse < Mongrel2::Response
|
|
184
185
|
### Get the length of the body IO. If the IO's offset is somewhere other than
|
185
186
|
### the beginning or end, the size of the remainder is used.
|
186
187
|
def get_content_length
|
187
|
-
if self.
|
188
|
-
|
189
|
-
elsif self.body.pos.nonzero? && !self.body.eof?
|
188
|
+
if self.body.pos.nonzero? && !self.body.eof?
|
189
|
+
self.log.info "Calculating content length based on an offset of %d" % [ self.body.pos ]
|
190
190
|
return self.body.size - self.body.pos
|
191
191
|
else
|
192
|
+
self.log.debug "Calculating body size via %p" % [ self.body.method(:size) ]
|
192
193
|
return self.body.size
|
193
194
|
end
|
194
195
|
end
|
data/lib/mongrel2/testing.rb
CHANGED
@@ -12,13 +12,38 @@ require 'mongrel2/request'
|
|
12
12
|
|
13
13
|
module Mongrel2
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
# A collection of helper functions that are generally useful
|
16
|
+
# for testing Mongrel2::Handlers.
|
17
17
|
module SpecHelpers
|
18
18
|
end # module SpecHelpers
|
19
19
|
|
20
20
|
|
21
|
-
|
21
|
+
# A factory for generating Mongrel2::Request objects for testing.
|
22
|
+
#
|
23
|
+
# Usage:
|
24
|
+
#
|
25
|
+
# require 'mongrel2/testing'
|
26
|
+
#
|
27
|
+
# describe "MyHandler" do
|
28
|
+
# before( :all ) do
|
29
|
+
# @factory = Mongrel2::RequestFactory.
|
30
|
+
# new( sender_id: 'my-handler',
|
31
|
+
# route: '/api/v1',
|
32
|
+
# headers: {accept: 'application/json'} )
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# before( :each ) do
|
36
|
+
# @app = MyHandler.new( 'my-handler', 'tcp://0.0.0.0:5556',
|
37
|
+
# 'tcp://0.0.0.0:5555' )
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# it "handles a JSON request for GET /" do
|
41
|
+
# request = @factory.get( '/api/v1' )
|
42
|
+
# response = @app.dispatch_request( request )
|
43
|
+
# #...
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
22
47
|
class RequestFactory
|
23
48
|
extend Loggability
|
24
49
|
|
@@ -218,14 +243,11 @@ module Mongrel2
|
|
218
243
|
end # RequestFactory
|
219
244
|
|
220
245
|
|
221
|
-
|
246
|
+
# A factory for generating WebSocket request objects for testing.
|
247
|
+
#
|
222
248
|
class WebSocketFrameFactory < Mongrel2::RequestFactory
|
223
|
-
extend Loggability
|
224
249
|
include Mongrel2::Constants
|
225
250
|
|
226
|
-
# Loggability API -- set up logging under the 'mongrel2' log host
|
227
|
-
log_to :mongrel2
|
228
|
-
|
229
251
|
# The default host
|
230
252
|
DEFAULT_TESTING_HOST = 'localhost'
|
231
253
|
DEFAULT_TESTING_PORT = '8113'
|
@@ -261,6 +283,8 @@ module Mongrel2
|
|
261
283
|
:headers => DEFAULT_TESTING_HEADERS,
|
262
284
|
}
|
263
285
|
|
286
|
+
DEFAULT_HANDSHAKE_BODY = 'GR7M5bFPiY2GvVc5a7CIMErQ18Q='
|
287
|
+
|
264
288
|
# Freeze all testing constants
|
265
289
|
constants.each do |cname|
|
266
290
|
const_get(cname).freeze
|
@@ -285,6 +309,26 @@ module Mongrel2
|
|
285
309
|
public
|
286
310
|
######
|
287
311
|
|
312
|
+
### Create an initial websocket handshake request and return it.
|
313
|
+
def handshake( uri, *subprotocols )
|
314
|
+
raise "Request doesn't route through %p" % [ self.route ] unless
|
315
|
+
uri.start_with?( self.route )
|
316
|
+
|
317
|
+
headers = if subprotocols.last.is_a?( Hash ) then subprotocols.pop else {} end
|
318
|
+
headers = self.make_merged_headers( uri, 0, headers )
|
319
|
+
headers.delete( :flags )
|
320
|
+
|
321
|
+
unless subprotocols.empty?
|
322
|
+
protos = subprotocols.map( &:to_s ).join( ', ' )
|
323
|
+
headers.sec_websocket_protocol = protos
|
324
|
+
end
|
325
|
+
|
326
|
+
rclass = Mongrel2::Request.subclass_for_method( :WEBSOCKET_HANDSHAKE )
|
327
|
+
|
328
|
+
return rclass.new( self.sender_id, self.conn_id.to_s, self.route, headers, DEFAULT_HANDSHAKE_BODY.dup )
|
329
|
+
end
|
330
|
+
|
331
|
+
|
288
332
|
### Create a new request with the specified +uri+, +data+, and +flags+.
|
289
333
|
def create( uri, data, *flags )
|
290
334
|
raise "Request doesn't route through %p" % [ self.route ] unless
|
data/lib/mongrel2/websocket.rb
CHANGED
@@ -9,6 +9,11 @@ require 'mongrel2/constants'
|
|
9
9
|
#
|
10
10
|
# class WebSocketEchoServer
|
11
11
|
#
|
12
|
+
# def handle_websocket_handshake( handshake )
|
13
|
+
# # :TODO: Sub-protocol/protocol version checks?
|
14
|
+
# return handshake.response
|
15
|
+
# end
|
16
|
+
#
|
12
17
|
# def handle_websocket( frame )
|
13
18
|
#
|
14
19
|
# # Close connections that send invalid frames
|
@@ -191,6 +196,93 @@ module Mongrel2::WebSocket
|
|
191
196
|
# Exception raised when a frame is malformed, doesn't parse, or is otherwise invalid.
|
192
197
|
class FrameError < Mongrel2::WebSocket::Error; end
|
193
198
|
|
199
|
+
# Exception raised when a handshake is created with an unrequested sub-protocol.
|
200
|
+
class HandshakeError < Mongrel2::WebSocket::Error; end
|
201
|
+
|
202
|
+
|
203
|
+
# The client (request) handshake for a WebSocket opening handshake.
|
204
|
+
class ClientHandshake < Mongrel2::HTTPRequest
|
205
|
+
include Mongrel2::WebSocket::Constants
|
206
|
+
|
207
|
+
# Set this class as the one that will handle WEBSOCKET_HANDSHAKE requests
|
208
|
+
register_request_type( self, :WEBSOCKET_HANDSHAKE )
|
209
|
+
|
210
|
+
|
211
|
+
### Override the type of response returned by this request type. Since
|
212
|
+
### websocket handshakes are symmetrical, responses are just new
|
213
|
+
### Mongrel2::WebSocket::Handshakes with the same Mongrel2 sender
|
214
|
+
### and connection IDs.
|
215
|
+
def self::response_class
|
216
|
+
return Mongrel2::WebSocket::ServerHandshake
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
######
|
221
|
+
public
|
222
|
+
######
|
223
|
+
|
224
|
+
### The list of protocols in the handshake's Sec-WebSocket-Protocol header
|
225
|
+
### as an Array of Strings.
|
226
|
+
def protocols
|
227
|
+
return ( self.headers.sec_websocket_protocol || '' ).split( /\s*,\s*/ )
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
### Create a Mongrel2::WebSocket::Handshake that will respond to the same server/connection as
|
232
|
+
### the receiver.
|
233
|
+
def response( protocol=nil )
|
234
|
+
@response = super() unless @response
|
235
|
+
if protocol
|
236
|
+
raise Mongrel2::WebSocket::HandshakeError,
|
237
|
+
"attempt to create a %s handshake which isn't supported by the client." %
|
238
|
+
[ protocol ] unless self.protocols.include?( protocol.to_s )
|
239
|
+
@response.protocols = protocol
|
240
|
+
end
|
241
|
+
|
242
|
+
return @response
|
243
|
+
end
|
244
|
+
|
245
|
+
end # class ClientHandshake
|
246
|
+
|
247
|
+
|
248
|
+
# The server (response) handshake for a WebSocket opening handshake.
|
249
|
+
class ServerHandshake < Mongrel2::HTTPResponse
|
250
|
+
include Mongrel2::WebSocket::Constants
|
251
|
+
|
252
|
+
### Create a server handshake frame from the given client +handshake+.
|
253
|
+
def self::from_request( handshake )
|
254
|
+
self.log.debug "Creating the server handshake for client handshake %p" % [ handshake ]
|
255
|
+
response = super
|
256
|
+
response.body.truncate( 0 )
|
257
|
+
|
258
|
+
# Mongrel2 puts the negotiated key in the body of the request
|
259
|
+
response.headers.sec_websocket_accept = handshake.body.read
|
260
|
+
|
261
|
+
# Set up the other typical server handshake values
|
262
|
+
response.status = HTTP::SWITCHING_PROTOCOLS
|
263
|
+
response.header.upgrade = 'websocket'
|
264
|
+
response.header.connection = 'Upgrade'
|
265
|
+
|
266
|
+
return response
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
### The list of protocols in the handshake's Sec-WebSocket-Protocol header
|
271
|
+
### as an Array of Strings.
|
272
|
+
def protocols
|
273
|
+
return ( self.headers.sec_websocket_protocol || '' ).split( /\s*,\s*/ )
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
### Set the list of protocols in the handshake's Sec-WebSocket-Protocol header.
|
278
|
+
def protocols=( new_protocols )
|
279
|
+
value = Array( new_protocols ).join( ', ' )
|
280
|
+
self.headers.sec_websocket_protocol = value
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
end # class ServerHandshake
|
285
|
+
|
194
286
|
|
195
287
|
# WebSocket frame class; this is used for both requests and responses in
|
196
288
|
# WebSocket services.
|
@@ -207,7 +299,8 @@ module Mongrel2::WebSocket
|
|
207
299
|
|
208
300
|
### Override the type of response returned by this request type. Since
|
209
301
|
### WebSocket connections are symmetrical, responses are just new
|
210
|
-
###
|
302
|
+
### Mongrel2::WebSocket::Frames with the same Mongrel2 sender and
|
303
|
+
### connection IDs.
|
211
304
|
def self::response_class
|
212
305
|
return self
|
213
306
|
end
|
@@ -448,10 +541,13 @@ module Mongrel2::WebSocket
|
|
448
541
|
end
|
449
542
|
|
450
543
|
|
451
|
-
### Create a
|
452
|
-
###
|
453
|
-
###
|
454
|
-
###
|
544
|
+
### Create a frame in response to the receiving Frame (i.e., with the same
|
545
|
+
### Mongrel2 connection ID and sender). This automatically sets up the correct
|
546
|
+
### status, Sec-WebSocket-Accept:, Connection, and Upgrade: headers based on the
|
547
|
+
### receiver. If +protocol+ is non-nil, and it matches one of the
|
548
|
+
### values listed in 'Sec-WebSocket-Protocol', it will be set as the
|
549
|
+
### Handshake's Sec-WebSocket-Protocol header. If it is non-nil, but doesn't
|
550
|
+
### match one of the request's values, a Mongrel2::WebSocket::Error is raised.
|
455
551
|
def response( *flags )
|
456
552
|
unless @response
|
457
553
|
@response = super()
|
data/spec/lib/constants.rb
CHANGED
@@ -109,6 +109,54 @@ module Mongrel2::TestConstants # :nodoc:all
|
|
109
109
|
}
|
110
110
|
|
111
111
|
|
112
|
+
#
|
113
|
+
# WebSocket frame constants
|
114
|
+
#
|
115
|
+
|
116
|
+
TEST_WEBSOCKET_PATH = '/ws'
|
117
|
+
|
118
|
+
TEST_WEBSOCKET_HEADERS = {
|
119
|
+
'connection' => 'Upgrade',
|
120
|
+
'FLAGS' => '0x8A',
|
121
|
+
'host' => 'host.example.com:80',
|
122
|
+
'METHOD' => 'WEBSOCKET',
|
123
|
+
'origin' => 'http://host.example.com:80',
|
124
|
+
'PATH' => TEST_WEBSOCKET_PATH,
|
125
|
+
'PATTERN' => TEST_WEBSOCKET_PATH,
|
126
|
+
'sec-websocket-extensions' => 'x-webkit-deflate-frame',
|
127
|
+
'sec-websocket-key' => 'SQvDVdT2SbgTg6P/lSZo7Q==',
|
128
|
+
'sec-websocket-protocol' => 'echo',
|
129
|
+
'sec-websocket-version' => '13',
|
130
|
+
'upgrade' => 'websocket',
|
131
|
+
'URI' => TEST_WEBSOCKET_PATH,
|
132
|
+
'VERSION' => 'HTTP/1.1',
|
133
|
+
'x-forwarded-for' => '127.0.0.2',
|
134
|
+
}
|
135
|
+
TEST_WEBSOCKET_HANDSHAKE_HEADERS = {
|
136
|
+
'connection' => 'Upgrade',
|
137
|
+
'host' => 'host.example.com:80',
|
138
|
+
'METHOD' => 'WEBSOCKET_HANDSHAKE',
|
139
|
+
'origin' => 'http://host.example.com:80',
|
140
|
+
'PATH' => TEST_WEBSOCKET_PATH,
|
141
|
+
'PATTERN' => TEST_WEBSOCKET_PATH,
|
142
|
+
'sec-websocket-extensions' => 'x-webkit-deflate-frame',
|
143
|
+
'sec-websocket-key' => 'SQvDVdT2SbgTg6P/lSZo7Q==',
|
144
|
+
'sec-websocket-protocol' => 'echo',
|
145
|
+
'sec-websocket-version' => '13',
|
146
|
+
'upgrade' => 'websocket',
|
147
|
+
'URI' => TEST_WEBSOCKET_PATH,
|
148
|
+
'VERSION' => 'HTTP/1.1',
|
149
|
+
'x-forwarded-for' => '127.0.0.2',
|
150
|
+
}
|
151
|
+
TEST_WEBSOCKET_BODY = 'GR7M5bFPiY2GvVc5a7CIMErQ18Q='
|
152
|
+
TEST_WEBSOCKET_REQUEST_OPTS = {
|
153
|
+
:uuid => TEST_UUID,
|
154
|
+
:id => TEST_ID,
|
155
|
+
:path => TEST_WEBSOCKET_PATH,
|
156
|
+
:body => '',
|
157
|
+
}
|
158
|
+
|
159
|
+
|
112
160
|
#
|
113
161
|
# HTTP constants
|
114
162
|
#
|
data/spec/lib/helpers.rb
CHANGED
@@ -114,13 +114,14 @@ module Mongrel2::SpecHelpers
|
|
114
114
|
bodystring = TNetstring.dump( opts[:body] || '' )
|
115
115
|
|
116
116
|
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
117
|
-
|
117
|
+
data = "%s %d %s %s%s" % [
|
118
118
|
opts[:uuid],
|
119
119
|
opts[:id],
|
120
120
|
opts[:path],
|
121
121
|
headerstring,
|
122
122
|
bodystring,
|
123
123
|
]
|
124
|
+
return data.encode( 'binary' )
|
124
125
|
end
|
125
126
|
|
126
127
|
|
@@ -134,13 +135,14 @@ module Mongrel2::SpecHelpers
|
|
134
135
|
bodystring = TNetstring.dump( opts[:body] || '' )
|
135
136
|
|
136
137
|
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
137
|
-
|
138
|
+
data = "%s %d %s %s%s" % [
|
138
139
|
opts[:uuid],
|
139
140
|
opts[:id],
|
140
141
|
opts[:path],
|
141
142
|
headerstring,
|
142
143
|
bodystring,
|
143
144
|
]
|
145
|
+
return data.encode( 'binary' )
|
144
146
|
end
|
145
147
|
|
146
148
|
|
@@ -156,13 +158,14 @@ module Mongrel2::SpecHelpers
|
|
156
158
|
bodystring = TNetstring.dump( Yajl::Encoder.encode(opts[:body] || []) )
|
157
159
|
|
158
160
|
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
159
|
-
|
161
|
+
data = "%s %d %s %s%s" % [
|
160
162
|
opts[:uuid],
|
161
163
|
opts[:id],
|
162
164
|
opts[:path],
|
163
165
|
headerstring,
|
164
166
|
bodystring,
|
165
167
|
]
|
168
|
+
return data.encode( 'binary' )
|
166
169
|
end
|
167
170
|
|
168
171
|
|
@@ -178,13 +181,56 @@ module Mongrel2::SpecHelpers
|
|
178
181
|
bodystring = TNetstring.dump( opts[:body] || "#{TEST_XML_PATH} />" )
|
179
182
|
|
180
183
|
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
181
|
-
|
184
|
+
data = "%s %d %s %s%s" % [
|
182
185
|
opts[:uuid],
|
183
186
|
opts[:id],
|
184
187
|
opts[:path],
|
185
188
|
headerstring,
|
186
189
|
bodystring,
|
187
190
|
]
|
191
|
+
return data.encode( 'binary' )
|
192
|
+
end
|
193
|
+
|
194
|
+
### Make a Mongrel2 handshake request for a WebSocket route.
|
195
|
+
def make_websocket_handshake( opts={} )
|
196
|
+
opts = TEST_WEBSOCKET_REQUEST_OPTS.merge( opts )
|
197
|
+
headers = normalize_headers( opts, TEST_WEBSOCKET_HANDSHAKE_HEADERS )
|
198
|
+
|
199
|
+
Mongrel2.log.debug "WebSocket start handshake, headers = %p, opts = %p" % [ headers, opts ]
|
200
|
+
|
201
|
+
headerstring = TNetstring.dump( Yajl::Encoder.encode(headers) )
|
202
|
+
bodystring = TNetstring.dump( opts[:body] || TEST_WEBSOCKET_BODY )
|
203
|
+
|
204
|
+
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
205
|
+
data = "%s %d %s %s%s" % [
|
206
|
+
opts[:uuid],
|
207
|
+
opts[:id],
|
208
|
+
opts[:path],
|
209
|
+
headerstring,
|
210
|
+
bodystring,
|
211
|
+
]
|
212
|
+
return data.encode( 'binary' )
|
213
|
+
end
|
214
|
+
|
215
|
+
### Make a Mongrel2 frame for a WebSocket route.
|
216
|
+
def make_websocket_frame( opts={} )
|
217
|
+
opts = TEST_WEBSOCKET_REQUEST_OPTS.merge( opts )
|
218
|
+
headers = normalize_headers( opts, TEST_WEBSOCKET_HEADERS )
|
219
|
+
|
220
|
+
Mongrel2.log.debug "WebSocket frame, headers = %p, opts = %p" % [ headers, opts ]
|
221
|
+
|
222
|
+
headerstring = TNetstring.dump( Yajl::Encoder.encode(headers) )
|
223
|
+
bodystring = TNetstring.dump( opts[:body] )
|
224
|
+
|
225
|
+
# UUID ID PATH SIZE:HEADERS,SIZE:BODY,
|
226
|
+
data = "%s %d %s %s%s" % [
|
227
|
+
opts[:uuid],
|
228
|
+
opts[:id],
|
229
|
+
opts[:path],
|
230
|
+
headerstring,
|
231
|
+
bodystring,
|
232
|
+
]
|
233
|
+
return data.encode( 'binary' )
|
188
234
|
end
|
189
235
|
|
190
236
|
end
|
@@ -209,6 +209,42 @@ describe Mongrel2::Handler do
|
|
209
209
|
response.should be_a( Mongrel2::Response )
|
210
210
|
end
|
211
211
|
|
212
|
+
it "dispatches WebSocket opening handshakes to the #handle_websocket_handshake method" do
|
213
|
+
ws_handler = Class.new( OneShotHandler ) do
|
214
|
+
def handle_websocket_handshake( handshake )
|
215
|
+
return handshake.response
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
req = make_websocket_handshake()
|
220
|
+
@request_sock.should_receive( :recv ).and_return( req )
|
221
|
+
|
222
|
+
res = ws_handler.new( TEST_UUID, TEST_SEND_SPEC, TEST_RECV_SPEC ).run
|
223
|
+
|
224
|
+
res.transactions.should have( 1 ).member
|
225
|
+
request, response = res.transactions.first
|
226
|
+
request.should be_a( Mongrel2::WebSocket::ClientHandshake )
|
227
|
+
response.should be_a( Mongrel2::WebSocket::ServerHandshake )
|
228
|
+
end
|
229
|
+
|
230
|
+
it "dispatches WebSocket protocol frames to the #handle_websocket method" do
|
231
|
+
ws_handler = Class.new( OneShotHandler ) do
|
232
|
+
def handle_websocket( frame )
|
233
|
+
return frame.response
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
req = make_websocket_frame()
|
238
|
+
@request_sock.should_receive( :recv ).and_return( req )
|
239
|
+
|
240
|
+
res = ws_handler.new( TEST_UUID, TEST_SEND_SPEC, TEST_RECV_SPEC ).run
|
241
|
+
|
242
|
+
res.transactions.should have( 1 ).member
|
243
|
+
request, response = res.transactions.first
|
244
|
+
request.should be_a( Mongrel2::WebSocket::Frame )
|
245
|
+
response.should be_a( Mongrel2::WebSocket::Frame )
|
246
|
+
end
|
247
|
+
|
212
248
|
it "continues when a ZMQ::Error is received but the connection remains open" do
|
213
249
|
req = make_request()
|
214
250
|
|
@@ -55,7 +55,7 @@ describe Mongrel2::HTTPResponse do
|
|
55
55
|
it "returns an empty response if its status is set to NO_CONTENT" do
|
56
56
|
@response.puts "The response body"
|
57
57
|
@response.status = HTTP::NO_CONTENT
|
58
|
-
@response.header_data.
|
58
|
+
@response.header_data.should_not =~ /Content-length/i
|
59
59
|
@response.header_data.should_not =~ /Content-type/i
|
60
60
|
@response.to_s.should_not =~ /The response body/i
|
61
61
|
end
|
@@ -69,9 +69,10 @@ describe Mongrel2::HTTPResponse do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
it "re-calculates the automatically-added headers when re-rendered" do
|
72
|
-
@response.header_data.should =~ /Content-length: 0/i
|
72
|
+
@response.header_data.should =~ /Content-length: 0\b/i
|
73
|
+
@response.status = HTTP::OK
|
73
74
|
@response << "More data!"
|
74
|
-
@response.header_data.should =~ /Content-length: 10/i
|
75
|
+
@response.header_data.should =~ /Content-length: 10\b/i
|
75
76
|
end
|
76
77
|
|
77
78
|
it "doesn't have a body" do
|
@@ -53,6 +53,69 @@ describe Mongrel2::WebSocket do
|
|
53
53
|
BINARY_DATA.force_encoding( Encoding::ASCII_8BIT )
|
54
54
|
|
55
55
|
|
56
|
+
describe 'ClientHandshake' do
|
57
|
+
|
58
|
+
it "is the registered request type for WEBSOCKET_HANDSHAKE requests" do
|
59
|
+
Mongrel2::Request.request_types[:WEBSOCKET_HANDSHAKE].should == Mongrel2::WebSocket::ClientHandshake
|
60
|
+
end
|
61
|
+
|
62
|
+
it "knows what subprotocols were requested" do
|
63
|
+
handshake = @factory.handshake( '/websock', 'echo', 'superecho' )
|
64
|
+
handshake.protocols.should == ['echo', 'superecho']
|
65
|
+
end
|
66
|
+
|
67
|
+
it "doesn't error if no subprotocols were requested" do
|
68
|
+
handshake = @factory.handshake( '/websock' )
|
69
|
+
handshake.protocols.should == []
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can create a response WebSocket::ServerHandshake for itself" do
|
73
|
+
handshake = @factory.handshake( '/websock' )
|
74
|
+
result = handshake.response
|
75
|
+
handshake.body.rewind
|
76
|
+
|
77
|
+
result.should be_a( Mongrel2::WebSocket::ServerHandshake )
|
78
|
+
result.sender_id.should == handshake.sender_id
|
79
|
+
result.conn_id.should == handshake.conn_id
|
80
|
+
result.header.sec_websocket_accept.should == handshake.body.string
|
81
|
+
result.status.should == HTTP::SWITCHING_PROTOCOLS
|
82
|
+
result.header.connection.should =~ /upgrade/i
|
83
|
+
result.header.upgrade.should =~ /websocket/i
|
84
|
+
|
85
|
+
result.body.rewind
|
86
|
+
result.body.read.should == ''
|
87
|
+
end
|
88
|
+
|
89
|
+
it "can create a response WebSocket::ServerHandshake with a valid sub-protocol for itself" do
|
90
|
+
handshake = @factory.handshake( '/websock', 'echo', 'superecho' )
|
91
|
+
result = handshake.response( :superecho )
|
92
|
+
handshake.body.rewind
|
93
|
+
|
94
|
+
result.should be_a( Mongrel2::WebSocket::ServerHandshake )
|
95
|
+
result.sender_id.should == handshake.sender_id
|
96
|
+
result.conn_id.should == handshake.conn_id
|
97
|
+
result.header.sec_websocket_accept.should == handshake.body.string
|
98
|
+
result.status.should == HTTP::SWITCHING_PROTOCOLS
|
99
|
+
result.header.connection.should =~ /upgrade/i
|
100
|
+
result.header.upgrade.should =~ /websocket/i
|
101
|
+
result.protocols.should == ['superecho']
|
102
|
+
|
103
|
+
result.body.rewind
|
104
|
+
result.body.read.should == ''
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises an exception if the specified protocol is not one of the client's advertised ones" do
|
108
|
+
handshake = @factory.handshake( '/websock', 'echo', 'superecho' )
|
109
|
+
|
110
|
+
expect {
|
111
|
+
handshake.response( :map_updates )
|
112
|
+
}.to raise_error( Mongrel2::WebSocket::HandshakeError, /map_updates/i )
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
|
56
119
|
describe 'Frame' do
|
57
120
|
|
58
121
|
it "is the registered request type for WEBSOCKET requests" do
|
@@ -310,5 +373,6 @@ describe Mongrel2::WebSocket do
|
|
310
373
|
|
311
374
|
end
|
312
375
|
|
376
|
+
|
313
377
|
end
|
314
378
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongrel2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.27.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
YUhDS0xaZFNLai9SSHVUT3QrZ2JsUmV4OEZBaDhOZUEKY21saFhlNDZwWk5K
|
37
37
|
Z1dLYnhaYWg4NWpJang5NWhSOHZPSStOQU01aUg5a09xSzEzRHJ4YWNUS1Bo
|
38
38
|
cWo1UGp3RgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
39
|
-
date: 2012-
|
39
|
+
date: 2012-07-02 00:00:00.000000000 Z
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: nokogiri
|
@@ -328,6 +328,7 @@ files:
|
|
328
328
|
- Rakefile
|
329
329
|
- bin/m2sh.rb
|
330
330
|
- data/mongrel2/bootstrap.html
|
331
|
+
- data/mongrel2/config.rb.in
|
331
332
|
- data/mongrel2/config.sql
|
332
333
|
- data/mongrel2/css/master.css
|
333
334
|
- data/mongrel2/js/websock-test.js
|
metadata.gz.sig
CHANGED
Binary file
|