serverside 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/CHANGELOG +22 -16
  2. data/Rakefile +11 -4
  3. data/bin/serverside +1 -1
  4. data/doc/rdoc/classes/Daemon.html +4 -4
  5. data/doc/rdoc/classes/ServerSide.html +10 -10
  6. data/doc/rdoc/classes/ServerSide/HTTP.html +5 -6
  7. data/doc/rdoc/classes/ServerSide/HTTP/Connection.html +24 -22
  8. data/doc/rdoc/classes/ServerSide/HTTP/Request.html +367 -121
  9. data/doc/rdoc/classes/ServerSide/Router.html +58 -58
  10. data/doc/rdoc/classes/ServerSide/StaticFiles.html +60 -49
  11. data/doc/rdoc/classes/ServerSide/StaticFiles/Const.html +12 -2
  12. data/doc/rdoc/classes/ServerSide/Template.html +12 -12
  13. data/doc/rdoc/created.rid +1 -1
  14. data/doc/rdoc/files/CHANGELOG.html +36 -31
  15. data/doc/rdoc/files/COPYING.html +1 -1
  16. data/doc/rdoc/files/README.html +1 -1
  17. data/doc/rdoc/files/lib/serverside/application_rb.html +1 -1
  18. data/doc/rdoc/files/lib/serverside/cluster_rb.html +1 -1
  19. data/doc/rdoc/files/lib/serverside/connection_rb.html +1 -1
  20. data/doc/rdoc/files/lib/serverside/core_ext_rb.html +1 -1
  21. data/doc/rdoc/files/lib/serverside/daemon_rb.html +1 -1
  22. data/doc/rdoc/files/lib/serverside/request_rb.html +8 -1
  23. data/doc/rdoc/files/lib/serverside/routing_rb.html +1 -1
  24. data/doc/rdoc/files/lib/serverside/server_rb.html +1 -1
  25. data/doc/rdoc/files/lib/serverside/static_rb.html +8 -1
  26. data/doc/rdoc/files/lib/serverside/template_rb.html +1 -1
  27. data/doc/rdoc/files/lib/serverside_rb.html +1 -1
  28. data/doc/rdoc/fr_class_index.html +0 -1
  29. data/doc/rdoc/fr_method_index.html +21 -20
  30. data/lib/serverside/connection.rb +6 -4
  31. data/lib/serverside/request.rb +101 -61
  32. data/lib/serverside/static.rb +22 -7
  33. data/test/functional/request_body_test.rb +93 -0
  34. data/test/functional/routing_server.rb +5 -5
  35. data/test/functional/routing_server_test.rb +3 -3
  36. data/test/spec/connection_spec.rb +61 -0
  37. data/test/spec/core_ext_spec.rb +20 -1
  38. data/test/unit/connection_test.rb +9 -9
  39. data/test/unit/request_test.rb +55 -55
  40. data/test/unit/server_test.rb +1 -1
  41. data/test/unit/static_test.rb +24 -25
  42. metadata +60 -60
  43. data/doc/rdoc/classes/ServerSide/HTTP/Const.html +0 -257
@@ -1,3 +1,5 @@
1
+ require 'time'
2
+
1
3
  module ServerSide
2
4
  # This module provides functionality for serving files and directory listings
3
5
  # over HTTP.
@@ -9,8 +11,10 @@ module ServerSide
9
11
  CacheControl = 'Cache-Control'.freeze
10
12
  MaxAge = "max-age=#{86400 * 30}".freeze
11
13
  IfNoneMatch = 'If-None-Match'.freeze
12
- NotModifiedClose = "HTTP/1.1 304 Not Modified\r\nConnection: close\r\nContent-Length: 0\r\nETag: %s\r\nCache-Control: #{MaxAge}\r\n\r\n".freeze
13
- NotModifiedPersist = "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\nETag: %s\r\nCache-Control: #{MaxAge}\r\n\r\n".freeze
14
+ IfModifiedSince = 'If-Modified-Since'.freeze
15
+ LastModified = "Last-Modified".freeze
16
+ NotModifiedClose = "HTTP/1.1 304 Not Modified\r\nDate: %s\r\nConnection: close\r\nLast-Modified: %s\r\nETag: %s\r\nCache-Control: #{MaxAge}\r\n\r\n".freeze
17
+ NotModifiedPersist = "HTTP/1.1 304 Not Modified\r\nDate: %s\r\nLast-Modified: %s\r\nETag: %s\r\nCache-Control: #{MaxAge}\r\n\r\n".freeze
14
18
  TextPlain = 'text/plain'.freeze
15
19
  TextHTML = 'text/html'.freeze
16
20
  MaxCacheFileSize = 100000.freeze # 100KB for the moment
@@ -43,7 +47,15 @@ module ServerSide
43
47
  def serve_file(fn)
44
48
  stat = File.stat(fn)
45
49
  etag = (Const::ETagFormat % [stat.mtime.to_i, stat.size, stat.ino]).freeze
46
- unless etag == @headers[Const::IfNoneMatch]
50
+ date = stat.mtime.httpdate
51
+
52
+ etag_match = @headers[Const::IfNoneMatch]
53
+ last_date = @headers[Const::IfModifiedSince]
54
+
55
+ modified = (!etag_match && !last_date) ||
56
+ (etag_match && (etag != etag_match)) || (last_date && (last_date != date))
57
+
58
+ if modified
47
59
  if @@static_files[fn] && (@@static_files[fn][0] == etag)
48
60
  content = @@static_files[fn][1]
49
61
  else
@@ -51,11 +63,14 @@ module ServerSide
51
63
  @@static_files[fn] = [etag.freeze, content]
52
64
  end
53
65
 
54
- send_response(200, @@mime_types[File.extname(fn)], content, stat.size,
55
- {Const::ETag => etag, Const::CacheControl => Const::MaxAge})
66
+ send_response(200, @@mime_types[File.extname(fn)], content, stat.size, {
67
+ Const::ETag => etag,
68
+ Const::LastModified => date,
69
+ Const::CacheControl => Const::MaxAge
70
+ })
56
71
  else
57
- @conn << ((@persistent ? Const::NotModifiedPersist :
58
- Const::NotModifiedClose) % etag)
72
+ @socket << ((@persistent ? Const::NotModifiedPersist :
73
+ Const::NotModifiedClose) % [Time.now.httpdate, date, etag, stat.size])
59
74
  end
60
75
  rescue => e
61
76
  send_response(404, Const::TextPlain, 'Error reading file.')
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'net/http'
3
+
4
+ $r = nil
5
+ ServerSide::Router.route(:path => '/') {$r = self; send_response(200, 'text', 'OK')}
6
+ t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17651, ServerSide::Router)}
7
+ sleep 0.1
8
+
9
+ class RequestBodyTest < Test::Unit::TestCase
10
+
11
+ def test_basic
12
+ h = Net::HTTP.new('localhost', 17651)
13
+
14
+ resp = h.post('/try', 'q=node_state&f=xml', {'Content-Type' => 'application/x-www-form-urlencoded'})
15
+ assert_equal 'OK', resp.body
16
+ assert_not_nil $r
17
+ assert_equal :post, $r.method
18
+ assert_not_nil $r.body
19
+ assert_equal 'q=node_state&f=xml', $r.body
20
+ assert_equal 'application/x-www-form-urlencoded', $r.content_type
21
+ assert_equal 18, $r.content_length
22
+ end
23
+
24
+ def text_to_multipart(key, value)
25
+ return "Content-Disposition: form-data; name=\"#{key.uri_escape}\"\r\n\r\n#{value}\r\n"
26
+ end
27
+
28
+ def file_to_multipart(key, filename, mime_type, content)
29
+ return "Content-Disposition: form-data; name=\"#{key.uri_escape}\"; filename=\"#{filename}\"\r\n" +
30
+ "Content-Transfer-Encoding: binary\r\nContent-Type: #{mime_type}\r\n\r\n#{content}\r\n"
31
+ end
32
+
33
+ def upload_file(*fns)
34
+ params = []
35
+ fns.each_with_index do |fn, idx|
36
+ params << file_to_multipart("file#{idx}", File.basename(fn),
37
+ "text/#{File.extname(fn).gsub('.', '')}", IO.read(fn))
38
+ end
39
+
40
+ boundary = '349832898984244898448024464570528145'
41
+ query =
42
+ params.map{|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
43
+ Net::HTTP.start('localhost', 17651).
44
+ post2("/", query, "Content-type" => "multipart/form-data; boundary=" + boundary)
45
+ end
46
+
47
+ def test_upload
48
+ upload_file(__FILE__)
49
+ assert_equal :post, $r.method
50
+ assert_not_nil $r.parameters[:file0]
51
+ assert_equal "text/rb", $r.parameters[:file0][:type]
52
+ assert_equal IO.read(__FILE__), $r.parameters[:file0][:content]
53
+ end
54
+
55
+ def test_multiple_uploads
56
+ readme_fn = File.dirname(__FILE__) + '/../../README'
57
+ upload_file(__FILE__, readme_fn)
58
+ assert_equal :post, $r.method
59
+ assert_not_nil $r.parameters[:file0]
60
+ assert_not_nil $r.parameters[:file1]
61
+ assert_equal "text/rb", $r.parameters[:file0][:type]
62
+ assert_equal "text/", $r.parameters[:file1][:type]
63
+ assert_equal IO.read(__FILE__), $r.parameters[:file0][:content]
64
+ assert_equal IO.read(readme_fn), $r.parameters[:file1][:content]
65
+ end
66
+
67
+ def test_mixed_form_data
68
+ params = [
69
+ file_to_multipart("file", File.basename(__FILE__), "text/rb", IO.read(__FILE__)),
70
+ text_to_multipart('warning','1'),
71
+ text_to_multipart('profile','css2'),
72
+ text_to_multipart('usermedium','all')
73
+ ]
74
+
75
+ boundary = '349832898984244898448024464570528145'
76
+ query =
77
+ params.map{|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
78
+ Net::HTTP.start('localhost', 17651).
79
+ post2("/", query, "Content-type" => "multipart/form-data; boundary=" + boundary)
80
+
81
+ assert_equal :post, $r.method
82
+
83
+ assert_not_nil $r.parameters[:file]
84
+ assert_equal "text/rb", $r.parameters[:file][:type]
85
+ assert_equal File.basename(__FILE__), $r.parameters[:file][:filename]
86
+ assert_equal IO.read(__FILE__), $r.parameters[:file][:content]
87
+
88
+ assert_equal '1', $r.parameters[:warning]
89
+ assert_equal 'css2', $r.parameters[:profile]
90
+ assert_equal 'all', $r.parameters[:usermedium]
91
+ end
92
+ end
93
+
@@ -3,12 +3,12 @@ require 'fileutils'
3
3
 
4
4
  FileUtils.cd(File.dirname(__FILE__))
5
5
 
6
- trap('TERM') {exit}
6
+ trap('INT') {exit}
7
7
 
8
- ServerSide::route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
9
- ServerSide::route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
10
- ServerSide.route(:path => '/xml/:flavor/feed.xml') do
8
+ ServerSide::Router.route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
9
+ ServerSide::Router.route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
10
+ ServerSide::Router.route(:path => '/xml') do
11
11
  redirect('http://feeds.feedburner.com/RobbyOnRails')
12
12
  end
13
13
 
14
- ServerSide::HTTP::Server.new('0.0.0.0', 8000, ServerSide::Router)
14
+ ServerSide::HTTP::Server.new('0.0.0.0', 4401, ServerSide::Router)
@@ -3,9 +3,9 @@ require 'net/http'
3
3
 
4
4
  class StaticServerTest < Test::Unit::TestCase
5
5
  def test_basic
6
- ServerSide::route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
7
- ServerSide::route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
8
- ServerSide.route(:path => '/xml/:flavor/feed.xml') do
6
+ ServerSide::Router.route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
7
+ ServerSide::Router.route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
8
+ ServerSide::Router.route(:path => '/xml/:flavor/feed.xml') do
9
9
  redirect('http://feeds.feedburner.com/RobbyOnRails')
10
10
  end
11
11
 
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), '../../lib/serverside')
2
+
3
+ # String extensions
4
+
5
+ class ServerSide::HTTP::Connection
6
+ attr_reader :socket, :request_class, :thread
7
+ end
8
+
9
+ $pause_request = false
10
+
11
+ class DummyRequest1 < ServerSide::HTTP::Request
12
+ @@instance_count = 0
13
+
14
+ def initialize(socket)
15
+ @@instance_count += 1
16
+ super(socket)
17
+ end
18
+
19
+ def self.instance_count
20
+ @@instance_count
21
+ end
22
+
23
+ def process
24
+ sleep 0.1 while $pause_request
25
+
26
+ @socket[:count] ||= 0
27
+ @socket[:count] += 1
28
+ @socket[:count] < 1000
29
+ end
30
+ end
31
+
32
+ class DummySocket < Hash
33
+ attr_accessor :closed
34
+ def close; @closed = true; end
35
+ end
36
+
37
+ include ServerSide::HTTP
38
+
39
+ context "Connection.initialize" do
40
+ specify "should take two parameters: socket and request_class" do
41
+ proc {Connection.new}.should_raise ArgumentError
42
+ proc {Connection.new(nil)}.should_raise ArgumentError
43
+ s = 'socket'
44
+ r = 'request_class'
45
+ c = Connection.new(s, r)
46
+ c.socket.should_be s
47
+ c.request_class.should_be r
48
+ end
49
+
50
+ specify "should spawn a thread that invokes Connection.process" do
51
+ $pause_request = true
52
+ c = Connection.new(DummySocket.new, DummyRequest1)
53
+ c.thread.should_be_an_instance_of Thread
54
+ c.thread.alive?.should_equal true
55
+ DummyRequest1.instance_count.should_equal 1
56
+ $pause_request = false
57
+ sleep 0.1 while c.thread.alive?
58
+ DummyRequest1.instance_count.should_equal 1000
59
+ end
60
+ end
61
+
@@ -60,9 +60,28 @@ context "Proc.proc_tag" do
60
60
  @l1.proc_tag.should_equal @l1.proc_tag
61
61
  end
62
62
 
63
- specify "should return the object_hash in base 36 prefixed with 'proc_'" do
63
+ specify "should return the object id in base 36 prefixed with 'proc_'" do
64
64
  @l1.proc_tag.should_equal 'proc_' + @l1.object_id.to_s(36).sub('-', '_')
65
65
  end
66
66
  end
67
67
 
68
+ context "Object.const_tag" do
69
+ setup do
70
+ @o1 = Object.new
71
+ @o2 = Object.new
72
+ end
68
73
 
74
+ specify "should return a unique tag for the object" do
75
+ @o1.const_tag.should_not_equal @o2.const_tag
76
+ end
77
+
78
+ specify "should return the same tag always" do
79
+ @o1.const_tag.should_equal @o1.const_tag
80
+ @o2.const_tag.should_equal @o2.const_tag
81
+ end
82
+
83
+ specify "should return the object id in base 36 (upcase) prefixed with 'C'" do
84
+ @o1.const_tag.should_equal 'C' + @o1.object_id.to_s(36).upcase.sub('-', '_')
85
+ @o2.const_tag.should_equal 'C' + @o2.object_id.to_s(36).upcase.sub('-', '_')
86
+ end
87
+ end
@@ -3,15 +3,15 @@ require 'stringio'
3
3
 
4
4
  class ConnectionTest < Test::Unit::TestCase
5
5
  class ServerSide::HTTP::Connection
6
- attr_reader :conn, :request_class, :thread
6
+ attr_reader :socket, :request_class, :thread
7
7
  end
8
8
 
9
9
  class DummyRequest1 < ServerSide::HTTP::Request
10
10
  @@instance_count = 0
11
11
 
12
- def initialize(conn)
12
+ def initialize(socket)
13
13
  @@instance_count += 1
14
- super(conn)
14
+ super(socket)
15
15
  end
16
16
 
17
17
  def self.instance_count
@@ -19,13 +19,13 @@ class ConnectionTest < Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  def process
22
- @conn[:count] ||= 0
23
- @conn[:count] += 1
24
- @conn[:count] < 1000
22
+ @socket[:count] ||= 0
23
+ @socket[:count] += 1
24
+ @socket[:count] < 1000
25
25
  end
26
26
  end
27
27
 
28
- class DummyConn < Hash
28
+ class DummySocket < Hash
29
29
  attr_accessor :closed
30
30
  def close; @closed = true; end
31
31
  end
@@ -33,11 +33,11 @@ class ConnectionTest < Test::Unit::TestCase
33
33
  def test_new
34
34
  r = ServerSide::HTTP::Connection.new('hello', ServerSide::HTTP::Request)
35
35
  sleep 0.1
36
- assert_equal 'hello', r.conn
36
+ assert_equal 'hello', r.socket
37
37
  assert_equal ServerSide::HTTP::Request, r.request_class
38
38
  assert_equal false, r.thread.alive?
39
39
 
40
- c = DummyConn.new
40
+ c = DummySocket.new
41
41
  r = ServerSide::HTTP::Connection.new(c, DummyRequest1)
42
42
  assert_equal DummyRequest1, r.request_class
43
43
  r.thread.join
@@ -42,7 +42,7 @@ class RequestTest < Test::Unit::TestCase
42
42
  end
43
43
 
44
44
  class ServerSide::HTTP::Request
45
- attr_writer :conn, :persistent
45
+ attr_writer :socket, :persistent
46
46
  end
47
47
 
48
48
  def test_process_result
@@ -68,25 +68,25 @@ class RequestTest < Test::Unit::TestCase
68
68
 
69
69
  def test_parse_request_invalid
70
70
  r = ServerSide::HTTP::Request.new(nil)
71
- r.conn = StringIO.new('POST /test')
71
+ r.socket = StringIO.new('POST /test')
72
72
  assert_nil r.parse
73
- r.conn = StringIO.new('invalid string')
73
+ r.socket = StringIO.new('invalid string')
74
74
  assert_nil r.parse
75
- r.conn = StringIO.new('GET HTTP/1.1')
75
+ r.socket = StringIO.new('GET HTTP/1.1')
76
76
  assert_nil r.parse
77
- r.conn = StringIO.new('GET /test http')
77
+ r.socket = StringIO.new('GET /test http')
78
78
  assert_nil r.parse
79
- r.conn = StringIO.new('GET /test HTTP')
79
+ r.socket = StringIO.new('GET /test HTTP')
80
80
  assert_nil r.parse
81
- r.conn = StringIO.new('GET /test HTTP/')
81
+ r.socket = StringIO.new('GET /test HTTP/')
82
82
  assert_nil r.parse
83
- r.conn = StringIO.new('POST /test HTTP/1.1')
83
+ r.socket = StringIO.new('POST /test HTTP/1.1')
84
84
  assert_nil r.parse
85
85
  end
86
86
 
87
87
  def test_parse_request
88
88
  r = ServerSide::HTTP::Request.new(nil)
89
- r.conn = StringIO.new("POST /test HTTP/1.1\r\n\r\n")
89
+ r.socket = StringIO.new("POST /test HTTP/1.1\r\n\r\n")
90
90
  assert_kind_of Hash, r.parse
91
91
  assert_equal :post, r.method
92
92
  assert_equal '/test', r.path
@@ -100,7 +100,7 @@ class RequestTest < Test::Unit::TestCase
100
100
 
101
101
  # trailing slash in path
102
102
  r = ServerSide::HTTP::Request.new(nil)
103
- r.conn = StringIO.new("POST /test/asdf/qw/?time=24%20hours HTTP/1.1\r\n\r\n")
103
+ r.socket = StringIO.new("POST /test/asdf/qw/?time=24%20hours HTTP/1.1\r\n\r\n")
104
104
  assert_kind_of Hash, r.parse
105
105
  assert_equal :post, r.method
106
106
  assert_equal '/test/asdf/qw', r.path
@@ -112,7 +112,7 @@ class RequestTest < Test::Unit::TestCase
112
112
  assert_nil r.response_cookies
113
113
  assert_equal true, r.persistent
114
114
 
115
- r.conn = StringIO.new("GET /cex?q=node_state HTTP/1.1\r\n\r\n")
115
+ r.socket = StringIO.new("GET /cex?q=node_state HTTP/1.1\r\n\r\n")
116
116
  assert_kind_of Hash, r.parse
117
117
  assert_equal :get, r.method
118
118
  assert_equal '/cex', r.path
@@ -121,25 +121,25 @@ class RequestTest < Test::Unit::TestCase
121
121
  assert_equal({}, r.cookies)
122
122
  assert_nil r.response_cookies
123
123
 
124
- r.conn = StringIO.new("GET / HTTP/1.0\r\n\r\n")
124
+ r.socket = StringIO.new("GET / HTTP/1.0\r\n\r\n")
125
125
  assert_kind_of Hash, r.parse
126
126
  assert_equal false, r.persistent
127
127
  assert_equal({}, r.cookies)
128
128
  assert_nil r.response_cookies
129
129
 
130
- r.conn = StringIO.new("GET / HTTP/invalid\r\n\r\n")
130
+ r.socket = StringIO.new("GET / HTTP/invalid\r\n\r\n")
131
131
  assert_kind_of Hash, r.parse
132
132
  assert_equal 'invalid', r.version
133
133
  assert_equal false, r.persistent
134
134
 
135
- r.conn = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\n\r\n")
135
+ r.socket = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\n\r\n")
136
136
  assert_kind_of Hash, r.parse
137
137
  assert_equal '1.1', r.version
138
138
  assert_equal 'close', r.headers['Connection']
139
139
  assert_equal false, r.persistent
140
140
 
141
141
  # cookies
142
- r.conn = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\nCookie: abc=1342; def=7%2f4\r\n\r\n")
142
+ r.socket = StringIO.new("GET / HTTP/1.1\r\nConnection: close\r\nCookie: abc=1342; def=7%2f4\r\n\r\n")
143
143
  assert_kind_of Hash, r.parse
144
144
  assert_equal 'abc=1342; def=7%2f4', r.headers['Cookie']
145
145
  assert_equal '1342', r.cookies[:abc]
@@ -149,48 +149,48 @@ class RequestTest < Test::Unit::TestCase
149
149
  def test_send_response
150
150
  r = ServerSide::HTTP::Request.new(nil)
151
151
  # simple case
152
- r.conn = StringIO.new
152
+ r.socket = StringIO.new
153
153
  r.persistent = true
154
154
  r.send_response(200, 'text', 'Hello there!')
155
- r.conn.rewind
156
- assert_equal "HTTP/1.1 200\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
157
- r.conn.read
155
+ r.socket.rewind
156
+ assert_equal "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
157
+ r.socket.read
158
158
 
159
159
  # include content-length
160
- r.conn = StringIO.new
160
+ r.socket = StringIO.new
161
161
  r.persistent = true
162
162
  r.send_response(404, 'text/html', '<h1>404!</h1>', 10) # incorrect length
163
- r.conn.rewind
164
- assert_equal "HTTP/1.1 404\r\nContent-Type: text/html\r\nContent-Length: 10\r\n\r\n<h1>404!</h1>",
165
- r.conn.read
163
+ r.socket.rewind
164
+ assert_equal "HTTP/1.1 404\r\nDate: #{Time.now.httpdate}\r\nContent-Type: text/html\r\nContent-Length: 10\r\n\r\n<h1>404!</h1>",
165
+ r.socket.read
166
166
 
167
167
  # headers
168
- r.conn = StringIO.new
168
+ r.socket = StringIO.new
169
169
  r.persistent = true
170
170
  r.send_response(404, 'text/html', '<h1>404!</h1>', nil, {'ETag' => 'xxyyzz'})
171
- r.conn.rewind
172
- 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>",
173
- r.conn.read
171
+ r.socket.rewind
172
+ assert_equal "HTTP/1.1 404\r\nDate: #{Time.now.httpdate}\r\nContent-Type: text/html\r\nETag: xxyyzz\r\nContent-Length: 13\r\n\r\n<h1>404!</h1>",
173
+ r.socket.read
174
174
 
175
175
  # no body
176
- r.conn = StringIO.new
176
+ r.socket = StringIO.new
177
177
  r.persistent = true
178
178
  r.send_response(404, 'text/html', nil, nil, {'Set-Cookie' => 'abc=123'})
179
- r.conn.rewind
180
- assert_equal "HTTP/1.1 404\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\n",
181
- r.conn.read
179
+ r.socket.rewind
180
+ assert_equal "HTTP/1.1 404\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\n",
181
+ r.socket.read
182
182
  assert_equal false, r.persistent
183
183
 
184
184
  # not persistent
185
- r.conn = StringIO.new
185
+ r.socket = StringIO.new
186
186
  r.persistent = false
187
187
  r.send_response(200, 'text', 'Hello there!')
188
- r.conn.rewind
189
- assert_equal "HTTP/1.1 200\r\nConnection: close\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
190
- r.conn.read
188
+ r.socket.rewind
189
+ assert_equal "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
190
+ r.socket.read
191
191
 
192
192
  # socket error
193
- r.conn = nil
193
+ r.socket = nil
194
194
  r.persistent = true
195
195
  assert_nothing_raised {r.send_response(200, 'text', 'Hello there!')}
196
196
  assert_equal false, r.persistent
@@ -198,51 +198,51 @@ class RequestTest < Test::Unit::TestCase
198
198
 
199
199
  def test_stream
200
200
  r = ServerSide::HTTP::Request.new(nil)
201
- r.conn = StringIO.new
201
+ r.socket = StringIO.new
202
202
  r.stream 'hey there'
203
- r.conn.rewind
204
- assert_equal 'hey there', r.conn.read
203
+ r.socket.rewind
204
+ assert_equal 'hey there', r.socket.read
205
205
 
206
- r.conn = StringIO.new
206
+ r.socket = StringIO.new
207
207
  r.persistent = true
208
208
  r.send_response(404, 'text/html', nil, nil, {'Set-Cookie' => 'abc=123'})
209
209
  r.stream('hey there')
210
- r.conn.rewind
211
- assert_equal "HTTP/1.1 404\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\nhey there",
212
- r.conn.read
210
+ r.socket.rewind
211
+ assert_equal "HTTP/1.1 404\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: abc=123\r\n\r\nhey there",
212
+ r.socket.read
213
213
  end
214
214
 
215
215
  def test_redirect
216
216
  r = ServerSide::HTTP::Request.new(nil)
217
- r.conn = StringIO.new
217
+ r.socket = StringIO.new
218
218
  r.redirect('http://mau.com/132')
219
- r.conn.rewind
220
- assert_equal "HTTP/1.1 302\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n", r.conn.read
219
+ r.socket.rewind
220
+ assert_equal "HTTP/1.1 302\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n", r.socket.read
221
221
 
222
- r.conn = StringIO.new
222
+ r.socket = StringIO.new
223
223
  r.redirect('http://www.google.com/search?q=meaning%20of%20life', true)
224
- r.conn.rewind
225
- 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
224
+ r.socket.rewind
225
+ assert_equal "HTTP/1.1 301\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nLocation: http://www.google.com/search?q=meaning%20of%20life\r\n\r\n", r.socket.read
226
226
  end
227
227
 
228
228
  def test_set_cookie
229
229
  r = ServerSide::HTTP::Request.new(nil)
230
- r.conn = StringIO.new
230
+ r.socket = StringIO.new
231
231
  t = Time.now + 360
232
232
  r.set_cookie(:session, "ABCDEFG", t)
233
233
  assert_equal "Set-Cookie: session=ABCDEFG; path=/; expires=#{t.rfc2822}\r\n", r.response_cookies
234
234
  r.send_response(200, 'text', 'Hi!')
235
- r.conn.rewind
236
- 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
235
+ r.socket.rewind
236
+ assert_equal "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\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.socket.read
237
237
  end
238
238
 
239
239
  def test_delete_cookie
240
240
  r = ServerSide::HTTP::Request.new(nil)
241
- r.conn = StringIO.new
241
+ r.socket = StringIO.new
242
242
  r.delete_cookie(:session)
243
243
  assert_equal "Set-Cookie: session=; path=/; expires=#{Time.at(0).rfc2822}\r\n", r.response_cookies
244
244
  r.send_response(200, 'text', 'Hi!')
245
- r.conn.rewind
246
- 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
245
+ r.socket.rewind
246
+ assert_equal "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\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.socket.read
247
247
  end
248
248
  end