serverside 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +10 -0
- data/README +3 -3
- data/Rakefile +4 -4
- data/bin/serverside +4 -4
- data/doc/rdoc/classes/Daemon.html +4 -4
- data/doc/rdoc/classes/ServerSide.html +14 -74
- data/doc/rdoc/classes/ServerSide/Application.html +13 -13
- data/doc/rdoc/classes/ServerSide/{Connection.html → HTTP.html} +12 -18
- data/doc/rdoc/classes/ServerSide/HTTP/Connection.html +200 -0
- data/doc/rdoc/classes/ServerSide/{Connection → HTTP}/Const.html +6 -6
- data/doc/rdoc/classes/ServerSide/{Connection/Base.html → HTTP/Request.html} +213 -164
- data/doc/rdoc/classes/ServerSide/{Server.html → HTTP/Server.html} +22 -22
- data/doc/rdoc/classes/ServerSide/Router.html +496 -0
- data/doc/rdoc/classes/ServerSide/StaticFiles.html +31 -30
- data/doc/rdoc/classes/ServerSide/Template.html +12 -12
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/CHANGELOG.html +22 -4
- data/doc/rdoc/files/COPYING.html +1 -1
- data/doc/rdoc/files/README.html +4 -4
- data/doc/rdoc/files/lib/serverside/application_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/cluster_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/connection_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/core_ext_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/daemon_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/request_rb.html +101 -0
- data/doc/rdoc/files/lib/serverside/routing_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/server_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/static_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/template_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside_rb.html +1 -1
- data/doc/rdoc/fr_class_index.html +6 -5
- data/doc/rdoc/fr_file_index.html +1 -0
- data/doc/rdoc/fr_method_index.html +31 -31
- data/lib/serverside/application.rb +1 -1
- data/lib/serverside/connection.rb +12 -141
- data/lib/serverside/request.rb +150 -0
- data/lib/serverside/routing.rb +117 -126
- data/lib/serverside/server.rb +13 -12
- data/lib/serverside/static.rb +1 -1
- data/test/functional/primitive_static_server_test.rb +3 -3
- data/test/functional/routing_server.rb +1 -1
- data/test/functional/routing_server_test.rb +1 -1
- data/test/functional/static_profile.rb +1 -1
- data/test/functional/static_server_test.rb +2 -2
- data/test/unit/connection_test.rb +30 -224
- data/test/unit/request_test.rb +248 -0
- data/test/unit/routing_test.rb +4 -4
- data/test/unit/server_test.rb +10 -8
- data/test/unit/static_test.rb +1 -1
- metadata +61 -56
- data/doc/rdoc/classes/ServerSide/Connection/Router.html +0 -493
data/lib/serverside/routing.rb
CHANGED
@@ -1,140 +1,131 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'request')
|
2
|
+
|
1
3
|
module ServerSide
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
4
|
+
# The Router class defines a kind of connection that can route requests
|
5
|
+
# to different handlers based on rules that can be specified either by
|
6
|
+
# lambdas or by hashes that contain variable names corresponding to patterns.
|
7
|
+
#
|
8
|
+
# The simplest form of a routing rule specifies a path pattern:
|
9
|
+
#
|
10
|
+
# ServerSide.route('/static') {serve_static('.'/@path)}
|
11
|
+
#
|
12
|
+
# But you can also check for other attributes of the request:
|
13
|
+
#
|
14
|
+
# ServerSide.route(:path => '/static', :host => '^:subdomain\.mydomain') {
|
15
|
+
# serve_static(@parameters[:subdomain]/@path)
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# It also possible to pass a lambda as a rule:
|
19
|
+
#
|
20
|
+
# ServerSide.route(lambda {@headers['Agent'] =~ /Moz/}) {serve_static('moz'/@path)}
|
21
|
+
#
|
22
|
+
# Routing rules are evaluated in backwards, so the rules should be ordered
|
23
|
+
# from the general to the specific.
|
24
|
+
class Router < HTTP::Request
|
25
|
+
# Returns true if routes were defined.
|
26
|
+
def self.has_routes?
|
27
|
+
@@rules && !@@rules.empty? rescue false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds a routing rule. The normalized rule is a hash containing keys (acting
|
31
|
+
# as instance variable names) with patterns as values. If the rule is not a
|
32
|
+
# hash, it is normalized into a pattern checked against the request path.
|
33
|
+
# Pattern values can also be arrays, any member of which is checked as a
|
34
|
+
# pattern. The rule can also be a Proc or lambda which is run with the
|
35
|
+
# connection object's binding. A contrived example:
|
20
36
|
#
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# Converts a rule into an if statement. All keys in the rule are matched
|
54
|
-
# against their respective values.
|
55
|
-
def self.rule_to_statement(rule, block)
|
56
|
-
proc_tag = define_proc(&block)
|
57
|
-
if Proc === rule
|
58
|
-
cond = define_proc(&rule).to_s
|
59
|
-
else
|
60
|
-
cond = rule.to_a.map {|kv|
|
61
|
-
if Array === kv[1]
|
62
|
-
'(' + kv[1].map {|v| condition_part(kv[0], v)}.join('||') + ')'
|
63
|
-
else
|
64
|
-
condition_part(kv[0], kv[1])
|
65
|
-
end
|
66
|
-
}.join('&&')
|
67
|
-
end
|
68
|
-
"return #{proc_tag} if #{cond}\n"
|
37
|
+
# ServerSide.route(lambda{path = 'mypage'}) {serve_static('mypage.html')}
|
38
|
+
def self.route(rule, &block)
|
39
|
+
@@rules ||= []
|
40
|
+
rule = {:path => rule} unless (Hash === rule) || (Proc === rule)
|
41
|
+
@@rules.unshift [rule, block]
|
42
|
+
compile_rules
|
43
|
+
end
|
44
|
+
|
45
|
+
# Compiles all rules into a respond method that is invoked when a request
|
46
|
+
# is received.
|
47
|
+
def self.compile_rules
|
48
|
+
@@rules ||= []
|
49
|
+
code = @@rules.inject('lambda {') {|m, r| m << rule_to_statement(r[0], r[1])}
|
50
|
+
code << 'default_handler}'
|
51
|
+
define_method(:respond, &eval(code))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Converts a rule into an if statement. All keys in the rule are matched
|
55
|
+
# against their respective values.
|
56
|
+
def self.rule_to_statement(rule, block)
|
57
|
+
proc_tag = define_proc(&block)
|
58
|
+
if Proc === rule
|
59
|
+
cond = define_proc(&rule).to_s
|
60
|
+
else
|
61
|
+
cond = rule.to_a.map {|kv|
|
62
|
+
if Array === kv[1]
|
63
|
+
'(' + kv[1].map {|v| condition_part(kv[0], v)}.join('||') + ')'
|
64
|
+
else
|
65
|
+
condition_part(kv[0], kv[1])
|
66
|
+
end
|
67
|
+
}.join('&&')
|
69
68
|
end
|
69
|
+
"return #{proc_tag} if #{cond}\n"
|
70
|
+
end
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
# Pattern for finding parameters inside patterns. Parameters are parts of the
|
73
|
+
# pattern, which the routing pre-processor turns into sub-regexp that are
|
74
|
+
# used to extract parameter values from the pattern.
|
75
|
+
#
|
76
|
+
# For example, matching '/controller/show' against '/controller/:action' will
|
77
|
+
# give us @parameters[:action] #=> "show"
|
78
|
+
ParamRegexp = /(?::([a-z]+))/
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
cond = "(@#{key} =~ #{cache_constant(Regexp.new(value))})"
|
93
|
-
if p_count == 0
|
94
|
-
cond
|
95
|
-
else
|
96
|
-
tag = define_proc(&eval(
|
97
|
-
"lambda {if #{cond}\n#{p_parse}true\nelse\nfalse\nend}"))
|
98
|
-
"(#{tag})"
|
99
|
-
end
|
80
|
+
# Returns the condition part for the key and value specified. The key is the
|
81
|
+
# name of an instance variable and the value is a pattern to match against.
|
82
|
+
# If the pattern contains parameters (for example, /controller/:action,) the
|
83
|
+
# method creates a lambda for extracting the parameter values.
|
84
|
+
def self.condition_part(key, value)
|
85
|
+
p_parse, p_count = '', 0
|
86
|
+
while (String === value) && (value =~ ParamRegexp)
|
87
|
+
value = value.dup
|
88
|
+
p_name = $1
|
89
|
+
p_count += 1
|
90
|
+
value.sub!(ParamRegexp, '(.+)')
|
91
|
+
p_parse << "@parameters[:#{p_name}] = $#{p_count}\n"
|
100
92
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
93
|
+
cond = "(@#{key} =~ #{cache_constant(Regexp.new(value))})"
|
94
|
+
if p_count == 0
|
95
|
+
cond
|
96
|
+
else
|
97
|
+
tag = define_proc(&eval(
|
98
|
+
"lambda {if #{cond}\n#{p_parse}true\nelse\nfalse\nend}"))
|
99
|
+
"(#{tag})"
|
107
100
|
end
|
101
|
+
end
|
108
102
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
103
|
+
# Converts a proc into a method, returning the method's name (as a symbol)
|
104
|
+
def self.define_proc(&block)
|
105
|
+
tag = block.proc_tag
|
106
|
+
define_method(tag.to_sym, &block) unless instance_methods.include?(tag)
|
107
|
+
tag.to_sym
|
108
|
+
end
|
116
109
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
110
|
+
# Converts a value into a local constant and freezes it. Returns the
|
111
|
+
# constant's tag name
|
112
|
+
def self.cache_constant(value)
|
113
|
+
tag = value.const_tag
|
114
|
+
class_eval "#{tag} = #{value.inspect}.freeze" rescue nil
|
115
|
+
tag
|
116
|
+
end
|
122
117
|
|
123
|
-
|
124
|
-
|
125
|
-
|
118
|
+
# Sets the default handler for incoming requests.
|
119
|
+
def self.route_default(&block)
|
120
|
+
define_method(:default_handler, &block)
|
121
|
+
compile_rules
|
122
|
+
end
|
126
123
|
|
127
|
-
|
124
|
+
# Generic responder for unhandled requests.
|
125
|
+
def unhandled
|
126
|
+
send_response(403, 'text', 'No handler found.')
|
128
127
|
end
|
129
|
-
|
130
|
-
|
131
|
-
# Adds a routing rule. This is a convenience method.
|
132
|
-
def self.route(rule, &block)
|
133
|
-
Connection::Router.route(rule, &block)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Sets the default request handler. This is a convenience method.
|
137
|
-
def self.route_default(&block)
|
138
|
-
Connection::Router.route_default(&block)
|
128
|
+
|
129
|
+
alias_method :default_handler, :unhandled
|
139
130
|
end
|
140
131
|
end
|
data/lib/serverside/server.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
require 'socket'
|
2
2
|
|
3
3
|
module ServerSide
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
module HTTP
|
5
|
+
# The ServerSide HTTP server is designed to be fast and simple. It is also
|
6
|
+
# designed to support both HTTP 1.1 persistent connections, and HTTP streaming
|
7
|
+
# for applications which use Comet techniques.
|
8
|
+
class Server
|
9
|
+
# Creates a new server by opening a listening socket and starting an accept
|
10
|
+
# loop. When a new connection is accepted, a new instance of the
|
11
|
+
# supplied connection class is instantiated and passed the connection for
|
12
|
+
# processing.
|
13
|
+
def initialize(host, port, request_class)
|
14
|
+
@server = TCPServer.new(host, port)
|
15
|
+
loop {Connection.new(@server.accept, request_class)}
|
16
|
+
end
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
data/lib/serverside/static.rb
CHANGED
@@ -3,7 +3,7 @@ require 'net/http'
|
|
3
3
|
|
4
4
|
class StaticServerTest < Test::Unit::TestCase
|
5
5
|
|
6
|
-
class
|
6
|
+
class StaticRequest < ServerSide::HTTP::Request
|
7
7
|
def respond
|
8
8
|
begin
|
9
9
|
# set_cookie(:hello, 'world', Time.now + 30) if @cookies[:test] == 'hello'
|
@@ -21,7 +21,7 @@ class StaticServerTest < Test::Unit::TestCase
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_basic
|
24
|
-
@t = Thread.new {ServerSide::Server.new('0.0.0.0', 17654,
|
24
|
+
@t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17654, StaticRequest)}
|
25
25
|
sleep 0.1
|
26
26
|
|
27
27
|
h = Net::HTTP.new('localhost', 17654)
|
@@ -40,7 +40,7 @@ class StaticServerTest < Test::Unit::TestCase
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_cookies
|
43
|
-
@t = Thread.new {ServerSide::Server.new('0.0.0.0', 17655,
|
43
|
+
@t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17655, StaticRequest)}
|
44
44
|
sleep 0.1
|
45
45
|
|
46
46
|
h = Net::HTTP.new('localhost', 17655)
|
@@ -9,7 +9,7 @@ class StaticServerTest < Test::Unit::TestCase
|
|
9
9
|
redirect('http://feeds.feedburner.com/RobbyOnRails')
|
10
10
|
end
|
11
11
|
|
12
|
-
t = Thread.new {ServerSide::Server.new('0.0.0.0', 17654, ServerSide::
|
12
|
+
t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17654, ServerSide::Router)}
|
13
13
|
sleep 0.1
|
14
14
|
|
15
15
|
h = Net::HTTP.new('localhost', 17654)
|
@@ -3,14 +3,14 @@ require 'net/http'
|
|
3
3
|
|
4
4
|
class StaticServerTest < Test::Unit::TestCase
|
5
5
|
|
6
|
-
class
|
6
|
+
class StaticRequest < ServerSide::HTTP::Request
|
7
7
|
def respond
|
8
8
|
serve_static('.'/@path)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_basic
|
13
|
-
t = Thread.new {ServerSide::Server.new('0.0.0.0', 17654,
|
13
|
+
t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17654, StaticRequest)}
|
14
14
|
sleep 0.1
|
15
15
|
|
16
16
|
h = Net::HTTP.new('localhost', 17654)
|
@@ -2,241 +2,47 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|
2
2
|
require 'stringio'
|
3
3
|
|
4
4
|
class ConnectionTest < Test::Unit::TestCase
|
5
|
-
class
|
6
|
-
attr_reader :
|
7
|
-
|
8
|
-
def process
|
9
|
-
@count ||= 0
|
10
|
-
@count += 1
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_new
|
15
|
-
r = DummyConnection1.new('hello')
|
16
|
-
assert_equal 'hello', r.conn
|
17
|
-
assert_equal 1, r.count
|
5
|
+
class ServerSide::HTTP::Connection
|
6
|
+
attr_reader :conn, :request_class, :thread
|
18
7
|
end
|
19
8
|
|
20
|
-
class
|
21
|
-
|
22
|
-
|
23
|
-
def initialize
|
24
|
-
@opened = true
|
25
|
-
end
|
9
|
+
class DummyRequest1 < ServerSide::HTTP::Request
|
10
|
+
@@instance_count = 0
|
26
11
|
|
27
|
-
def
|
28
|
-
|
12
|
+
def initialize(conn)
|
13
|
+
@@instance_count += 1
|
14
|
+
super(conn)
|
29
15
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class DummyConnection2 < ServerSide::Connection::Base
|
33
|
-
attr_accessor :count, :persistent
|
34
16
|
|
35
|
-
def
|
36
|
-
|
37
|
-
@count += 1
|
38
|
-
@persistent = @count < 1000
|
17
|
+
def self.instance_count
|
18
|
+
@@instance_count
|
39
19
|
end
|
40
20
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def test_process
|
46
|
-
c = DummyConnection.new
|
47
|
-
r = DummyConnection2.new(c)
|
48
|
-
sleep 0.2
|
49
|
-
assert_equal false, c.opened
|
50
|
-
assert_equal 1000, r.count
|
51
|
-
end
|
52
|
-
|
53
|
-
class ServerSide::Connection::Base
|
54
|
-
attr_accessor :conn, :method, :query, :version, :path, :parameters,
|
55
|
-
:headers, :persistent, :cookies, :response_cookies
|
56
|
-
end
|
57
|
-
|
58
|
-
class DummyConnection3 < ServerSide::Connection::Base
|
59
|
-
def initialize
|
21
|
+
def process
|
22
|
+
@conn[:count] ||= 0
|
23
|
+
@conn[:count] += 1
|
24
|
+
@conn[:count] < 1000
|
60
25
|
end
|
61
26
|
end
|
62
27
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
assert_nil r.parse_request
|
67
|
-
r.conn = StringIO.new('invalid string')
|
68
|
-
assert_nil r.parse_request
|
69
|
-
r.conn = StringIO.new('GET HTTP/1.1')
|
70
|
-
assert_nil r.parse_request
|
71
|
-
r.conn = StringIO.new('GET /test http')
|
72
|
-
assert_nil r.parse_request
|
73
|
-
r.conn = StringIO.new('GET /test HTTP')
|
74
|
-
assert_nil r.parse_request
|
75
|
-
r.conn = StringIO.new('GET /test HTTP/')
|
76
|
-
assert_nil r.parse_request
|
77
|
-
r.conn = StringIO.new('POST /test HTTP/1.1')
|
78
|
-
assert_nil r.parse_request
|
28
|
+
class DummyConn < Hash
|
29
|
+
attr_accessor :closed
|
30
|
+
def close; @closed = true; end
|
79
31
|
end
|
80
|
-
|
81
|
-
def test_parse_request
|
82
|
-
r = DummyConnection3.new
|
83
|
-
r.conn = StringIO.new("POST /test HTTP/1.1\r\n\r\n")
|
84
|
-
assert_kind_of Hash, r.parse_request
|
85
|
-
assert_equal :post, r.method
|
86
|
-
assert_equal '/test', r.path
|
87
|
-
assert_nil r.query
|
88
|
-
assert_equal '1.1', r.version
|
89
|
-
assert_equal({}, r.parameters)
|
90
|
-
assert_equal({}, r.headers)
|
91
|
-
assert_equal({}, r.cookies)
|
92
|
-
assert_nil r.response_cookies
|
93
|
-
assert_equal true, r.persistent
|
94
32
|
|
95
|
-
|
96
|
-
r =
|
97
|
-
|
98
|
-
|
99
|
-
assert_equal
|
100
|
-
assert_equal
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
assert_equal
|
105
|
-
|
106
|
-
|
107
|
-
assert_equal
|
108
|
-
|
109
|
-
r.conn = StringIO.new("GET /cex?q=node_state HTTP/1.1\r\n\r\n")
|
110
|
-
assert_kind_of Hash, r.parse_request
|
111
|
-
assert_equal :get, r.method
|
112
|
-
assert_equal '/cex', r.path
|
113
|
-
assert_equal 'q=node_state', r.query
|
114
|
-
assert_equal({:q => 'node_state'}, r.parameters)
|
115
|
-
assert_equal({}, r.cookies)
|
116
|
-
assert_nil r.response_cookies
|
117
|
-
|
118
|
-
r.conn = StringIO.new("GET / HTTP/1.0\r\n\r\n")
|
119
|
-
assert_kind_of Hash, r.parse_request
|
120
|
-
assert_equal false, r.persistent
|
121
|
-
assert_equal({}, r.cookies)
|
122
|
-
assert_nil r.response_cookies
|
123
|
-
|
124
|
-
r.conn = StringIO.new("GET / HTTP/invalid\r\n\r\n")
|
125
|
-
assert_kind_of Hash, r.parse_request
|
126
|
-
assert_equal 'invalid', r.version
|
127
|
-
assert_equal false, r.persistent
|
128
|
-
|
129
|
-
r.conn = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\n\r\n")
|
130
|
-
assert_kind_of Hash, r.parse_request
|
131
|
-
assert_equal '1.1', r.version
|
132
|
-
assert_equal 'close', r.headers['Connection']
|
133
|
-
assert_equal false, r.persistent
|
134
|
-
|
135
|
-
# cookies
|
136
|
-
r.conn = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\nCookie: abc=1342; def=7%2f4\r\n\r\n")
|
137
|
-
assert_kind_of Hash, r.parse_request
|
138
|
-
assert_equal 'abc=1342; def=7%2f4', r.headers['Cookie']
|
139
|
-
assert_equal '1342', r.cookies[:abc]
|
140
|
-
assert_equal '7/4', r.cookies[:def]
|
141
|
-
end
|
142
|
-
|
143
|
-
def test_send_response
|
144
|
-
r = DummyConnection3.new
|
145
|
-
# simple case
|
146
|
-
r.conn = StringIO.new
|
147
|
-
r.persistent = true
|
148
|
-
r.send_response(200, 'text', 'Hello there!')
|
149
|
-
r.conn.rewind
|
150
|
-
assert_equal "HTTP/1.1 200\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
|
151
|
-
r.conn.read
|
152
|
-
|
153
|
-
# include content-length
|
154
|
-
r.conn = StringIO.new
|
155
|
-
r.persistent = true
|
156
|
-
r.send_response(404, 'text/html', '<h1>404!</h1>', 10) # incorrect length
|
157
|
-
r.conn.rewind
|
158
|
-
assert_equal "HTTP/1.1 404\r\nContent-Type: text/html\r\nContent-Length: 10\r\n\r\n<h1>404!</h1>",
|
159
|
-
r.conn.read
|
160
|
-
|
161
|
-
# headers
|
162
|
-
r.conn = StringIO.new
|
163
|
-
r.persistent = true
|
164
|
-
r.send_response(404, 'text/html', '<h1>404!</h1>', nil, {'ETag' => 'xxyyzz'})
|
165
|
-
r.conn.rewind
|
166
|
-
assert_equal "HTTP/1.1 404\r\nContent-Type: text/html\r\nETag: xxyyzz\r\nContent-Length: 13\r\n\r\n<h1>404!</h1>",
|
167
|
-
r.conn.read
|
168
|
-
|
169
|
-
# no body
|
170
|
-
r.conn = StringIO.new
|
171
|
-
r.persistent = true
|
172
|
-
r.send_response(404, 'text/html', nil, nil, {'Set-Cookie' => 'abc=123'})
|
173
|
-
r.conn.rewind
|
174
|
-
assert_equal "HTTP/1.1 404\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\n",
|
175
|
-
r.conn.read
|
176
|
-
assert_equal false, r.persistent
|
177
|
-
|
178
|
-
# not persistent
|
179
|
-
r.conn = StringIO.new
|
180
|
-
r.persistent = false
|
181
|
-
r.send_response(200, 'text', 'Hello there!')
|
182
|
-
r.conn.rewind
|
183
|
-
assert_equal "HTTP/1.1 200\r\nConnection: close\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
|
184
|
-
r.conn.read
|
185
|
-
|
186
|
-
# socket error
|
187
|
-
r.conn = nil
|
188
|
-
r.persistent = true
|
189
|
-
assert_nothing_raised {r.send_response(200, 'text', 'Hello there!')}
|
190
|
-
assert_equal false, r.persistent
|
191
|
-
end
|
192
|
-
|
193
|
-
def test_stream
|
194
|
-
r = DummyConnection3.new
|
195
|
-
r.conn = StringIO.new
|
196
|
-
r.stream 'hey there'
|
197
|
-
r.conn.rewind
|
198
|
-
assert_equal 'hey there', r.conn.read
|
199
|
-
|
200
|
-
r.conn = StringIO.new
|
201
|
-
r.persistent = true
|
202
|
-
r.send_response(404, 'text/html', nil, nil, {'Set-Cookie' => 'abc=123'})
|
203
|
-
r.stream('hey there')
|
204
|
-
r.conn.rewind
|
205
|
-
assert_equal "HTTP/1.1 404\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\nhey there",
|
206
|
-
r.conn.read
|
207
|
-
end
|
208
|
-
|
209
|
-
def test_redirect
|
210
|
-
r = DummyConnection3.new
|
211
|
-
r.conn = StringIO.new
|
212
|
-
r.redirect('http://mau.com/132')
|
213
|
-
r.conn.rewind
|
214
|
-
assert_equal "HTTP/1.1 302\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n", r.conn.read
|
215
|
-
|
216
|
-
r.conn = StringIO.new
|
217
|
-
r.redirect('http://www.google.com/search?q=meaning%20of%20life', true)
|
218
|
-
r.conn.rewind
|
219
|
-
assert_equal "HTTP/1.1 301\r\nConnection: close\r\nLocation: http://www.google.com/search?q=meaning%20of%20life\r\n\r\n", r.conn.read
|
220
|
-
end
|
221
|
-
|
222
|
-
def test_set_cookie
|
223
|
-
r = DummyConnection3.new
|
224
|
-
r.conn = StringIO.new
|
225
|
-
t = Time.now + 360
|
226
|
-
r.set_cookie(:session, "ABCDEFG", t)
|
227
|
-
assert_equal "Set-Cookie: session=ABCDEFG; path=/; expires=#{t.rfc2822}\r\n", r.response_cookies
|
228
|
-
r.send_response(200, 'text', 'Hi!')
|
229
|
-
r.conn.rewind
|
230
|
-
assert_equal "HTTP/1.1 200\r\nConnection: close\r\nContent-Type: text\r\nSet-Cookie: session=ABCDEFG; path=/; expires=#{t.rfc2822}\r\nContent-Length: 3\r\n\r\nHi!", r.conn.read
|
231
|
-
end
|
232
|
-
|
233
|
-
def test_delete_cookie
|
234
|
-
r = DummyConnection3.new
|
235
|
-
r.conn = StringIO.new
|
236
|
-
r.delete_cookie(:session)
|
237
|
-
assert_equal "Set-Cookie: session=; path=/; expires=#{Time.at(0).rfc2822}\r\n", r.response_cookies
|
238
|
-
r.send_response(200, 'text', 'Hi!')
|
239
|
-
r.conn.rewind
|
240
|
-
assert_equal "HTTP/1.1 200\r\nConnection: close\r\nContent-Type: text\r\nSet-Cookie: session=; path=/; expires=#{Time.at(0).rfc2822}\r\nContent-Length: 3\r\n\r\nHi!", r.conn.read
|
33
|
+
def test_new
|
34
|
+
r = ServerSide::HTTP::Connection.new('hello', ServerSide::HTTP::Request)
|
35
|
+
sleep 0.1
|
36
|
+
assert_equal 'hello', r.conn
|
37
|
+
assert_equal ServerSide::HTTP::Request, r.request_class
|
38
|
+
assert_equal false, r.thread.alive?
|
39
|
+
|
40
|
+
c = DummyConn.new
|
41
|
+
r = ServerSide::HTTP::Connection.new(c, DummyRequest1)
|
42
|
+
assert_equal DummyRequest1, r.request_class
|
43
|
+
r.thread.join
|
44
|
+
assert_equal 1000, c[:count]
|
45
|
+
assert_equal 1000, DummyRequest1.instance_count
|
46
|
+
assert_equal true, c.closed
|
241
47
|
end
|
242
48
|
end
|