serverside 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +10 -0
  2. data/README +3 -3
  3. data/Rakefile +4 -4
  4. data/bin/serverside +4 -4
  5. data/doc/rdoc/classes/Daemon.html +4 -4
  6. data/doc/rdoc/classes/ServerSide.html +14 -74
  7. data/doc/rdoc/classes/ServerSide/Application.html +13 -13
  8. data/doc/rdoc/classes/ServerSide/{Connection.html → HTTP.html} +12 -18
  9. data/doc/rdoc/classes/ServerSide/HTTP/Connection.html +200 -0
  10. data/doc/rdoc/classes/ServerSide/{Connection → HTTP}/Const.html +6 -6
  11. data/doc/rdoc/classes/ServerSide/{Connection/Base.html → HTTP/Request.html} +213 -164
  12. data/doc/rdoc/classes/ServerSide/{Server.html → HTTP/Server.html} +22 -22
  13. data/doc/rdoc/classes/ServerSide/Router.html +496 -0
  14. data/doc/rdoc/classes/ServerSide/StaticFiles.html +31 -30
  15. data/doc/rdoc/classes/ServerSide/Template.html +12 -12
  16. data/doc/rdoc/created.rid +1 -1
  17. data/doc/rdoc/files/CHANGELOG.html +22 -4
  18. data/doc/rdoc/files/COPYING.html +1 -1
  19. data/doc/rdoc/files/README.html +4 -4
  20. data/doc/rdoc/files/lib/serverside/application_rb.html +1 -1
  21. data/doc/rdoc/files/lib/serverside/cluster_rb.html +1 -1
  22. data/doc/rdoc/files/lib/serverside/connection_rb.html +1 -1
  23. data/doc/rdoc/files/lib/serverside/core_ext_rb.html +1 -1
  24. data/doc/rdoc/files/lib/serverside/daemon_rb.html +1 -1
  25. data/doc/rdoc/files/lib/serverside/request_rb.html +101 -0
  26. data/doc/rdoc/files/lib/serverside/routing_rb.html +1 -1
  27. data/doc/rdoc/files/lib/serverside/server_rb.html +1 -1
  28. data/doc/rdoc/files/lib/serverside/static_rb.html +1 -1
  29. data/doc/rdoc/files/lib/serverside/template_rb.html +1 -1
  30. data/doc/rdoc/files/lib/serverside_rb.html +1 -1
  31. data/doc/rdoc/fr_class_index.html +6 -5
  32. data/doc/rdoc/fr_file_index.html +1 -0
  33. data/doc/rdoc/fr_method_index.html +31 -31
  34. data/lib/serverside/application.rb +1 -1
  35. data/lib/serverside/connection.rb +12 -141
  36. data/lib/serverside/request.rb +150 -0
  37. data/lib/serverside/routing.rb +117 -126
  38. data/lib/serverside/server.rb +13 -12
  39. data/lib/serverside/static.rb +1 -1
  40. data/test/functional/primitive_static_server_test.rb +3 -3
  41. data/test/functional/routing_server.rb +1 -1
  42. data/test/functional/routing_server_test.rb +1 -1
  43. data/test/functional/static_profile.rb +1 -1
  44. data/test/functional/static_server_test.rb +2 -2
  45. data/test/unit/connection_test.rb +30 -224
  46. data/test/unit/request_test.rb +248 -0
  47. data/test/unit/routing_test.rb +4 -4
  48. data/test/unit/server_test.rb +10 -8
  49. data/test/unit/static_test.rb +1 -1
  50. metadata +61 -56
  51. data/doc/rdoc/classes/ServerSide/Connection/Router.html +0 -493
@@ -1,140 +1,131 @@
1
+ require File.join(File.dirname(__FILE__), 'request')
2
+
1
3
  module ServerSide
2
- module Connection
3
- # The Router class defines a kind of connection that can route requests
4
- # to different handlers based on rules that can be specified either by
5
- # lambdas or by hashes that contain variable names corresponding to patterns.
6
- #
7
- # The simplest form of a routing rule specifies a path pattern:
8
- #
9
- # ServerSide.route('/static') {serve_static('.'/@path)}
10
- #
11
- # But you can also check for other attributes of the request:
12
- #
13
- # ServerSide.route(:path => '/static', :host => '^:subdomain\.mydomain') {
14
- # serve_static(@parameters[:subdomain]/@path)
15
- # }
16
- #
17
- # It also possible to pass a lambda as a rule:
18
- #
19
- # ServerSide.route(lambda {@headers['Agent'] =~ /Moz/}) {serve_static('moz'/@path)}
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
- # Routing rules are evaluated in backwards, so the rules should be ordered
22
- # from the general to the specific.
23
- class Router < Base
24
- # Returns true if routes were defined.
25
- def self.has_routes?
26
- @@rules && !@@rules.empty? rescue false
27
- end
28
-
29
- # Adds a routing rule. The normalized rule is a hash containing keys (acting
30
- # as instance variable names) with patterns as values. If the rule is not a
31
- # hash, it is normalized into a pattern checked against the request path.
32
- # Pattern values can also be arrays, any member of which is checked as a
33
- # pattern. The rule can also be a Proc or lambda which is run with the
34
- # connection object's binding. A contrived example:
35
- #
36
- # ServerSide.route(lambda{path = 'mypage'}) {serve_static('mypage.html')}
37
- def self.route(rule, &block)
38
- @@rules ||= []
39
- rule = {:path => rule} unless (Hash === rule) || (Proc === rule)
40
- @@rules.unshift [rule, block]
41
- compile_rules
42
- end
43
-
44
- # Compiles all rules into a respond method that is invoked when a request
45
- # is received.
46
- def self.compile_rules
47
- @@rules ||= []
48
- code = @@rules.inject('lambda {') {|m, r| m << rule_to_statement(r[0], r[1])}
49
- code << 'default_handler}'
50
- define_method(:respond, &eval(code))
51
- end
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
- # Pattern for finding parameters inside patterns. Parameters are parts of the
72
- # pattern, which the routing pre-processor turns into sub-regexp that are
73
- # used to extract parameter values from the pattern.
74
- #
75
- # For example, matching '/controller/show' against '/controller/:action' will
76
- # give us @parameters[:action] #=> "show"
77
- ParamRegexp = /(?::([a-z]+))/
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
- # Returns the condition part for the key and value specified. The key is the
80
- # name of an instance variable and the value is a pattern to match against.
81
- # If the pattern contains parameters (for example, /controller/:action,) the
82
- # method creates a lambda for extracting the parameter values.
83
- def self.condition_part(key, value)
84
- p_parse, p_count = '', 0
85
- while (String === value) && (value =~ ParamRegexp)
86
- value = value.dup
87
- p_name = $1
88
- p_count += 1
89
- value.sub!(ParamRegexp, '(.+)')
90
- p_parse << "@parameters[:#{p_name}] = $#{p_count}\n"
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
- # Converts a proc into a method, returning the method's name (as a symbol)
103
- def self.define_proc(&block)
104
- tag = block.proc_tag
105
- define_method(tag.to_sym, &block) unless instance_methods.include?(tag)
106
- tag.to_sym
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
- # Converts a value into a local constant and freezes it. Returns the
110
- # constant's tag name
111
- def self.cache_constant(value)
112
- tag = value.const_tag
113
- class_eval "#{tag} = #{value.inspect}.freeze" rescue nil
114
- tag
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
- # Sets the default handler for incoming requests.
118
- def self.route_default(&block)
119
- define_method(:default_handler, &block)
120
- compile_rules
121
- end
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
- def unhandled
124
- send_response(403, 'text', 'No handler found.')
125
- end
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
- alias_method :default_handler, :unhandled
124
+ # Generic responder for unhandled requests.
125
+ def unhandled
126
+ send_response(403, 'text', 'No handler found.')
128
127
  end
129
- end
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
@@ -1,18 +1,19 @@
1
1
  require 'socket'
2
2
 
3
3
  module ServerSide
4
- # The ServerSide HTTP server is designed to be fast and simple. It is also
5
- # designed to support both HTTP 1.1 persistent connections, and HTTP streaming
6
- # for applications which use Comet techniques.
7
- class Server
8
- # Creates a new server by opening a listening socket and starting an accept
9
- # loop. When a new connection is accepted, a new instance of the
10
- # supplied connection class is instantiated and passed the connection for
11
- # processing.
12
- def initialize(host, port, connection_class)
13
- @connection_class = connection_class
14
- @server = TCPServer.new(host, port)
15
- loop {@connection_class.new(@server.accept)}
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
@@ -1,6 +1,6 @@
1
1
  module ServerSide
2
2
  # This module provides functionality for serving files and directory listings
3
- # over HTTP. It is mainly used by ServerSide::Connection::Static.
3
+ # over HTTP.
4
4
  module StaticFiles
5
5
  # Frozen constants to be used by the module.
6
6
  module Const
@@ -3,7 +3,7 @@ require 'net/http'
3
3
 
4
4
  class StaticServerTest < Test::Unit::TestCase
5
5
 
6
- class StaticConnection < ServerSide::Connection::Base
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, StaticConnection)}
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, StaticConnection)}
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)
@@ -11,4 +11,4 @@ ServerSide.route(:path => '/xml/:flavor/feed.xml') do
11
11
  redirect('http://feeds.feedburner.com/RobbyOnRails')
12
12
  end
13
13
 
14
- ServerSide::Server.new('0.0.0.0', 8000, ServerSide::Connection::Router)
14
+ ServerSide::HTTP::Server.new('0.0.0.0', 8000, ServerSide::Router)
@@ -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::Connection::Router)}
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)
@@ -7,7 +7,7 @@ FileUtils.cd(File.dirname(__FILE__))
7
7
  pid = fork do
8
8
  trap('TERM') {exit}
9
9
  require 'profile'
10
- ServerSide::Server.new('0.0.0.0', 8000, ServerSide::Connection::Static)
10
+ ServerSide::HTTP::Server.new('0.0.0.0', 8000, nil)
11
11
  end
12
12
 
13
13
  puts "Please wait..."
@@ -3,14 +3,14 @@ require 'net/http'
3
3
 
4
4
  class StaticServerTest < Test::Unit::TestCase
5
5
 
6
- class StaticServer < ServerSide::Connection::Base
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, StaticServer)}
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 DummyConnection1 < ServerSide::Connection::Base
6
- attr_reader :count, :conn
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 DummyConnection
21
- attr_reader :opened
22
-
23
- def initialize
24
- @opened = true
25
- end
9
+ class DummyRequest1 < ServerSide::HTTP::Request
10
+ @@instance_count = 0
26
11
 
27
- def close
28
- @opened = false
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 parse_request
36
- @count ||= 0
37
- @count += 1
38
- @persistent = @count < 1000
17
+ def self.instance_count
18
+ @@instance_count
39
19
  end
40
20
 
41
- def respond
42
- end
43
- end
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
- def test_parse_request_invalid
64
- r = DummyConnection3.new
65
- r.conn = StringIO.new('POST /test')
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
- # trailing slash in path
96
- r = DummyConnection3.new
97
- r.conn = StringIO.new("POST /test/asdf/qw/?time=24%20hours HTTP/1.1\r\n\r\n")
98
- assert_kind_of Hash, r.parse_request
99
- assert_equal :post, r.method
100
- assert_equal '/test/asdf/qw', r.path
101
- assert_equal 'time=24%20hours', r.query
102
- assert_equal '1.1', r.version
103
- assert_equal({:time => '24 hours'}, r.parameters)
104
- assert_equal({}, r.headers)
105
- assert_equal({}, r.cookies)
106
- assert_nil r.response_cookies
107
- assert_equal true, r.persistent
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