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.
- data/CHANGELOG +22 -16
- data/Rakefile +11 -4
- data/bin/serverside +1 -1
- data/doc/rdoc/classes/Daemon.html +4 -4
- data/doc/rdoc/classes/ServerSide.html +10 -10
- data/doc/rdoc/classes/ServerSide/HTTP.html +5 -6
- data/doc/rdoc/classes/ServerSide/HTTP/Connection.html +24 -22
- data/doc/rdoc/classes/ServerSide/HTTP/Request.html +367 -121
- data/doc/rdoc/classes/ServerSide/Router.html +58 -58
- data/doc/rdoc/classes/ServerSide/StaticFiles.html +60 -49
- data/doc/rdoc/classes/ServerSide/StaticFiles/Const.html +12 -2
- data/doc/rdoc/classes/ServerSide/Template.html +12 -12
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/CHANGELOG.html +36 -31
- data/doc/rdoc/files/COPYING.html +1 -1
- data/doc/rdoc/files/README.html +1 -1
- 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 +8 -1
- 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 +8 -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 +0 -1
- data/doc/rdoc/fr_method_index.html +21 -20
- data/lib/serverside/connection.rb +6 -4
- data/lib/serverside/request.rb +101 -61
- data/lib/serverside/static.rb +22 -7
- data/test/functional/request_body_test.rb +93 -0
- data/test/functional/routing_server.rb +5 -5
- data/test/functional/routing_server_test.rb +3 -3
- data/test/spec/connection_spec.rb +61 -0
- data/test/spec/core_ext_spec.rb +20 -1
- data/test/unit/connection_test.rb +9 -9
- data/test/unit/request_test.rb +55 -55
- data/test/unit/server_test.rb +1 -1
- data/test/unit/static_test.rb +24 -25
- metadata +60 -60
- data/doc/rdoc/classes/ServerSide/HTTP/Const.html +0 -257
data/lib/serverside/static.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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('
|
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
|
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',
|
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
|
+
|
data/test/spec/core_ext_spec.rb
CHANGED
@@ -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
|
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 :
|
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(
|
12
|
+
def initialize(socket)
|
13
13
|
@@instance_count += 1
|
14
|
-
super(
|
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
|
-
@
|
23
|
-
@
|
24
|
-
@
|
22
|
+
@socket[:count] ||= 0
|
23
|
+
@socket[:count] += 1
|
24
|
+
@socket[:count] < 1000
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
class
|
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.
|
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 =
|
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
|
data/test/unit/request_test.rb
CHANGED
@@ -42,7 +42,7 @@ class RequestTest < Test::Unit::TestCase
|
|
42
42
|
end
|
43
43
|
|
44
44
|
class ServerSide::HTTP::Request
|
45
|
-
attr_writer :
|
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.
|
71
|
+
r.socket = StringIO.new('POST /test')
|
72
72
|
assert_nil r.parse
|
73
|
-
r.
|
73
|
+
r.socket = StringIO.new('invalid string')
|
74
74
|
assert_nil r.parse
|
75
|
-
r.
|
75
|
+
r.socket = StringIO.new('GET HTTP/1.1')
|
76
76
|
assert_nil r.parse
|
77
|
-
r.
|
77
|
+
r.socket = StringIO.new('GET /test http')
|
78
78
|
assert_nil r.parse
|
79
|
-
r.
|
79
|
+
r.socket = StringIO.new('GET /test HTTP')
|
80
80
|
assert_nil r.parse
|
81
|
-
r.
|
81
|
+
r.socket = StringIO.new('GET /test HTTP/')
|
82
82
|
assert_nil r.parse
|
83
|
-
r.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
152
|
+
r.socket = StringIO.new
|
153
153
|
r.persistent = true
|
154
154
|
r.send_response(200, 'text', 'Hello there!')
|
155
|
-
r.
|
156
|
-
assert_equal "HTTP/1.1 200\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!",
|
157
|
-
r.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
185
|
+
r.socket = StringIO.new
|
186
186
|
r.persistent = false
|
187
187
|
r.send_response(200, 'text', 'Hello there!')
|
188
|
-
r.
|
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.
|
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.
|
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.
|
201
|
+
r.socket = StringIO.new
|
202
202
|
r.stream 'hey there'
|
203
|
-
r.
|
204
|
-
assert_equal 'hey there', r.
|
203
|
+
r.socket.rewind
|
204
|
+
assert_equal 'hey there', r.socket.read
|
205
205
|
|
206
|
-
r.
|
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.
|
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.
|
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.
|
217
|
+
r.socket = StringIO.new
|
218
218
|
r.redirect('http://mau.com/132')
|
219
|
-
r.
|
220
|
-
assert_equal "HTTP/1.1 302\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n", r.
|
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.
|
222
|
+
r.socket = StringIO.new
|
223
223
|
r.redirect('http://www.google.com/search?q=meaning%20of%20life', true)
|
224
|
-
r.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|