rufus-verbs 0.1
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/README.txt +248 -0
- data/lib/rufus/verbs.rb +74 -0
- data/lib/rufus/verbs/conditional.rb +142 -0
- data/lib/rufus/verbs/endpoint.rb +532 -0
- data/test/auth_test.rb +46 -0
- data/test/block_test.rb +49 -0
- data/test/conditional_test.rb +49 -0
- data/test/dryrun_test.rb +42 -0
- data/test/https_test.rb +42 -0
- data/test/iconditional_test.rb +68 -0
- data/test/items.rb +262 -0
- data/test/proxy_test.rb +82 -0
- data/test/redir_test.rb +26 -0
- data/test/simple_test.rb +74 -0
- data/test/test.rb +12 -0
- data/test/testbase.rb +47 -0
- metadata +70 -0
data/test/auth_test.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Sun Jan 13 12:33:03 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require 'testbase'
|
12
|
+
|
13
|
+
require 'rufus/verbs'
|
14
|
+
|
15
|
+
|
16
|
+
class AuthTest < Test::Unit::TestCase
|
17
|
+
include TestBaseMixin
|
18
|
+
|
19
|
+
include Rufus::Verbs
|
20
|
+
|
21
|
+
#
|
22
|
+
# Using an items server with the authentication on.
|
23
|
+
#
|
24
|
+
def setup
|
25
|
+
|
26
|
+
@server = ItemServer.new :auth => true
|
27
|
+
@server.start
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def test_0
|
32
|
+
|
33
|
+
#res = get :uri => "http://localhost:7777/items"
|
34
|
+
#assert_equal 200, res.code.to_i
|
35
|
+
#assert_equal "{}", res.body.strip
|
36
|
+
expect 401, nil, get(:uri => "http://localhost:7777/items")
|
37
|
+
|
38
|
+
expect 401, nil, get(
|
39
|
+
:uri => "http://localhost:7777/items",
|
40
|
+
:http_basic_authentication => [ "toto", "wrong" ])
|
41
|
+
|
42
|
+
expect 200, {}, get(
|
43
|
+
:uri => "http://localhost:7777/items?a",
|
44
|
+
:http_basic_authentication => [ "toto", "toto" ])
|
45
|
+
end
|
46
|
+
end
|
data/test/block_test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Sun Jan 13 20:02:25 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require 'testbase'
|
12
|
+
|
13
|
+
require 'rufus/verbs'
|
14
|
+
|
15
|
+
|
16
|
+
class BlockTest < Test::Unit::TestCase
|
17
|
+
include TestBaseMixin
|
18
|
+
|
19
|
+
include Rufus::Verbs
|
20
|
+
|
21
|
+
|
22
|
+
def test_0
|
23
|
+
|
24
|
+
res = post :uri => "http://localhost:7777/items/0" do
|
25
|
+
"Fedor" +
|
26
|
+
"Fedorovitch"
|
27
|
+
end
|
28
|
+
assert_equal 201, res.code.to_i
|
29
|
+
assert_equal "http://localhost:7777/items/0", res['Location']
|
30
|
+
|
31
|
+
|
32
|
+
expect(
|
33
|
+
200,
|
34
|
+
{ 0 => "FedorFedorovitch" },
|
35
|
+
get(:uri => "http://localhost:7777/things"))
|
36
|
+
|
37
|
+
res = post :uri => "http://localhost:7777/items/1" do |req|
|
38
|
+
# do whatever with the request [headers]
|
39
|
+
"John"
|
40
|
+
end
|
41
|
+
assert_equal 201, res.code.to_i
|
42
|
+
assert_equal "http://localhost:7777/items/1", res['Location']
|
43
|
+
|
44
|
+
expect(
|
45
|
+
200,
|
46
|
+
{ 0 => "FedorFedorovitch", 1 => "John" },
|
47
|
+
get(:uri => "http://localhost:7777/things"))
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Wed Jan 16 13:54:36 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require 'testbase'
|
12
|
+
|
13
|
+
require 'rufus/verbs'
|
14
|
+
|
15
|
+
|
16
|
+
class ConditionalTest < Test::Unit::TestCase
|
17
|
+
include TestBaseMixin
|
18
|
+
|
19
|
+
include Rufus::Verbs
|
20
|
+
|
21
|
+
|
22
|
+
def test_1
|
23
|
+
|
24
|
+
ep = ConditionalEndPoint.new(:host => "localhost", :port => 7777)
|
25
|
+
expect 200, {}, ep.get(:resource => "items")
|
26
|
+
|
27
|
+
res = ep.put :resource => "items", :id => 0 do
|
28
|
+
"blockdata"
|
29
|
+
end
|
30
|
+
assert_equal 404, res.code.to_i
|
31
|
+
|
32
|
+
res = ep.post :resource => "items", :id => 0 do
|
33
|
+
"blockdata"
|
34
|
+
end
|
35
|
+
assert_equal 201, res.code.to_i
|
36
|
+
assert_equal "http://localhost:7777/items/0", res['Location']
|
37
|
+
|
38
|
+
res = expect 200, { 0 => "blockdata" }, ep.get(:res => "items")
|
39
|
+
assert_kind_of Net::HTTPResponse, res
|
40
|
+
assert_respond_to res, :lastmod
|
41
|
+
i = res.object_id
|
42
|
+
#p res.to_hash
|
43
|
+
|
44
|
+
res = expect 200, { 0 => "blockdata" }, ep.get(:res => "items")
|
45
|
+
assert_equal i, res.object_id
|
46
|
+
|
47
|
+
assert_equal 1, ep.cache_current_size
|
48
|
+
end
|
49
|
+
end
|
data/test/dryrun_test.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Tue Jan 15 09:26:45 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
require 'rufus/verbs'
|
13
|
+
|
14
|
+
|
15
|
+
class DryRunTest < Test::Unit::TestCase
|
16
|
+
|
17
|
+
include Rufus::Verbs
|
18
|
+
|
19
|
+
|
20
|
+
def test_0
|
21
|
+
|
22
|
+
req = put(
|
23
|
+
:dry_run => true,
|
24
|
+
:uri => "http://localhost:7777/items/1",
|
25
|
+
:params => { "a" => "A", :b => :B })
|
26
|
+
|
27
|
+
assert "/items/1?a=A&b=B", req.path
|
28
|
+
|
29
|
+
req = put(
|
30
|
+
:dry_run => true,
|
31
|
+
:uri => "http://localhost:7777/items/1",
|
32
|
+
:query => { "a" => "A", :b => :B })
|
33
|
+
|
34
|
+
assert "/items/1?a=A&b=B", req.path
|
35
|
+
|
36
|
+
req = put(
|
37
|
+
"http://localhost:7777/items/1?a=A", :d => "toto", :dry_run => true)
|
38
|
+
|
39
|
+
assert "/items/1?a=A", req.path
|
40
|
+
assert "toto", req.body
|
41
|
+
end
|
42
|
+
end
|
data/test/https_test.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Mon Jan 14 00:07:38 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require 'testbase'
|
12
|
+
|
13
|
+
require 'rufus/verbs'
|
14
|
+
|
15
|
+
|
16
|
+
class HttpsTest < Test::Unit::TestCase
|
17
|
+
include TestBaseMixin
|
18
|
+
|
19
|
+
include Rufus::Verbs
|
20
|
+
|
21
|
+
def setup
|
22
|
+
# no need for an items server
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_0
|
29
|
+
|
30
|
+
res = expect(
|
31
|
+
200,
|
32
|
+
nil,
|
33
|
+
get(:uri => "http://jmettraux.wordpress.com/2006/03/31/cvs-down/"))
|
34
|
+
|
35
|
+
#res.each_header do |h|
|
36
|
+
# p h
|
37
|
+
#end
|
38
|
+
|
39
|
+
#puts res.body
|
40
|
+
assert res.body.match "^<!DOCTYPE html PUBLIC"
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Sun Jan 13 12:33:03 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require 'testbase'
|
12
|
+
|
13
|
+
require 'rufus/verbs'
|
14
|
+
|
15
|
+
|
16
|
+
class ItemConditionalTest < Test::Unit::TestCase
|
17
|
+
include TestBaseMixin
|
18
|
+
|
19
|
+
include Rufus::Verbs
|
20
|
+
|
21
|
+
|
22
|
+
def test_0
|
23
|
+
|
24
|
+
require 'open-uri'
|
25
|
+
f = open "http://localhost:7777/items"
|
26
|
+
d = f.read
|
27
|
+
f.close
|
28
|
+
|
29
|
+
assert_equal "{}", d.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_1
|
33
|
+
|
34
|
+
res = get :uri => "http://localhost:7777/items"
|
35
|
+
|
36
|
+
assert_equal 200, res.code.to_i
|
37
|
+
assert_equal "{}", res.body.strip
|
38
|
+
|
39
|
+
p res['Etag']
|
40
|
+
#p res['Last-Modified']
|
41
|
+
assert res['Etag'].length > 0
|
42
|
+
assert res['Last-Modified'].length > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_2
|
46
|
+
|
47
|
+
res = get "http://localhost:7777/items"
|
48
|
+
assert_equal 200, res.code.to_i
|
49
|
+
|
50
|
+
lm = res['Last-Modified']
|
51
|
+
etag = res['Etag']
|
52
|
+
|
53
|
+
res = get(
|
54
|
+
"http://localhost:7777/items",
|
55
|
+
:headers => { 'If-Modified-Since' => lm })
|
56
|
+
assert_equal 304, res.code.to_i
|
57
|
+
|
58
|
+
res = get(
|
59
|
+
"http://localhost:7777/items", :h => { 'If-None-Match' => etag })
|
60
|
+
assert_equal 304, res.code.to_i
|
61
|
+
|
62
|
+
res = get(
|
63
|
+
"http://localhost:7777/items",
|
64
|
+
:h => { 'If-Modified-Since' => lm, 'If-None-Match' => etag })
|
65
|
+
assert_equal 304, res.code.to_i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/test/items.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# an in-memory resource set for testing rufus-verbs
|
4
|
+
#
|
5
|
+
# jmettraux@gmail.com
|
6
|
+
#
|
7
|
+
# Fri Jan 11 12:36:45 JST 2008
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'date'
|
11
|
+
require 'webrick'
|
12
|
+
|
13
|
+
|
14
|
+
class LastModifiedHash
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
|
18
|
+
@hash = {}
|
19
|
+
touch
|
20
|
+
end
|
21
|
+
|
22
|
+
def touch (key=nil)
|
23
|
+
|
24
|
+
now = Time.now.httpdate
|
25
|
+
@hash[key] = now if key
|
26
|
+
@hash['__self'] = now
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete (key)
|
30
|
+
|
31
|
+
@hash.delete key
|
32
|
+
@hash['__self'] = Time.now.httpdate
|
33
|
+
end
|
34
|
+
|
35
|
+
def last_modified (key)
|
36
|
+
|
37
|
+
key = key || '__self'
|
38
|
+
@hash[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear
|
42
|
+
|
43
|
+
@hash.clear
|
44
|
+
@hash['__self'] = Time.now.httpdate
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
#
|
50
|
+
# This servlet provides a RESTful in-memory resource "/items".
|
51
|
+
#
|
52
|
+
class ItemServlet < WEBrick::HTTPServlet::AbstractServlet
|
53
|
+
|
54
|
+
@@items = {}
|
55
|
+
@@lastmod = LastModifiedHash.new
|
56
|
+
|
57
|
+
|
58
|
+
def initialize (server, *options)
|
59
|
+
|
60
|
+
super
|
61
|
+
@auth = server.auth
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Overriding the service() method to perform a potential auth check
|
66
|
+
#
|
67
|
+
def service (req, res)
|
68
|
+
|
69
|
+
WEBrick::HTTPAuth.basic_auth(req, res, "items") do |u, p|
|
70
|
+
(u != nil and u == p)
|
71
|
+
end if @auth
|
72
|
+
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
def do_GET (req, res)
|
77
|
+
|
78
|
+
i = item_id req
|
79
|
+
|
80
|
+
return reply(res, 404, "no item '#{i}'") \
|
81
|
+
if i and not items[i]
|
82
|
+
|
83
|
+
representation, et, lm = fetch_representation i
|
84
|
+
|
85
|
+
since = req['If-Modified-Since']
|
86
|
+
since = DateTime.parse(since) if since
|
87
|
+
match = req['If-None-Match']
|
88
|
+
|
89
|
+
if ((not since and not match) or
|
90
|
+
(since and (since > DateTime.parse(lm))) or
|
91
|
+
(match and (match != et)))
|
92
|
+
|
93
|
+
res['Etag'] = et
|
94
|
+
res['Last-Modified'] = lm
|
95
|
+
res.body = representation.inspect + "\n"
|
96
|
+
|
97
|
+
else
|
98
|
+
|
99
|
+
reply(res, 304, "Not Modified")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def do_POST (req, res)
|
104
|
+
|
105
|
+
query = WEBrick::HTTPUtils::parse_query(req.query_string)
|
106
|
+
m = query['_method']
|
107
|
+
m = m.downcase if m
|
108
|
+
return do_PUT(req, res) if m == 'put'
|
109
|
+
return do_DELETE(req, res) if m == 'delete'
|
110
|
+
|
111
|
+
i = item_id req
|
112
|
+
items[i] = req.body
|
113
|
+
lastmod.touch i
|
114
|
+
|
115
|
+
res['Location'] = "#{@host}/items/#{i}"
|
116
|
+
reply res, 201, "item created"
|
117
|
+
end
|
118
|
+
|
119
|
+
def do_PUT (req, res)
|
120
|
+
|
121
|
+
i = item_id req
|
122
|
+
|
123
|
+
return reply(res, 404, "no item '#{i}'") unless items[i]
|
124
|
+
|
125
|
+
items[i] = req.body
|
126
|
+
lastmod.touch i
|
127
|
+
|
128
|
+
reply res, 200, "item updated"
|
129
|
+
end
|
130
|
+
|
131
|
+
def do_DELETE (req, res)
|
132
|
+
|
133
|
+
i = item_id req
|
134
|
+
|
135
|
+
return reply(res, 404, "no item '#{i}'") unless items[i]
|
136
|
+
|
137
|
+
items.delete i
|
138
|
+
lastmod.delete i
|
139
|
+
|
140
|
+
reply res, 200, "item deleted"
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# clears the items
|
145
|
+
#
|
146
|
+
def self.flush
|
147
|
+
|
148
|
+
@@items.clear
|
149
|
+
@@lastmod.clear
|
150
|
+
end
|
151
|
+
|
152
|
+
protected
|
153
|
+
|
154
|
+
def items
|
155
|
+
@@items
|
156
|
+
end
|
157
|
+
|
158
|
+
def lastmod
|
159
|
+
@@lastmod
|
160
|
+
end
|
161
|
+
|
162
|
+
def is_modified (req, key)
|
163
|
+
|
164
|
+
since = req['If-Modified-Since']
|
165
|
+
match = req['If-None-Match']
|
166
|
+
|
167
|
+
return true unless since or match
|
168
|
+
|
169
|
+
puts
|
170
|
+
p [ since, match ]
|
171
|
+
puts
|
172
|
+
|
173
|
+
(since or match)
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Returns representation, etag, last_modified
|
178
|
+
#
|
179
|
+
def fetch_representation (key=nil)
|
180
|
+
|
181
|
+
representation = if key
|
182
|
+
items[key]
|
183
|
+
else
|
184
|
+
items
|
185
|
+
end
|
186
|
+
|
187
|
+
[ representation,
|
188
|
+
representation.inspect.hash.to_s,
|
189
|
+
lastmod.last_modified(key) ]
|
190
|
+
end
|
191
|
+
|
192
|
+
def reply (res, code, message)
|
193
|
+
|
194
|
+
res.status = code
|
195
|
+
res.body = message + "\n"
|
196
|
+
res['Content-type'] = "text/plain"
|
197
|
+
end
|
198
|
+
|
199
|
+
def item_id (req)
|
200
|
+
|
201
|
+
p = req.path_info[1..-1]
|
202
|
+
return nil if not p or p == ''
|
203
|
+
p.to_i
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
#
|
208
|
+
# just redirecting to the ItemServlet...
|
209
|
+
#
|
210
|
+
class ThingServlet < WEBrick::HTTPServlet::AbstractServlet
|
211
|
+
|
212
|
+
def do_GET (req, res)
|
213
|
+
|
214
|
+
res.set_redirect(
|
215
|
+
WEBrick::HTTPStatus[303],
|
216
|
+
"http://localhost:7777/items")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# Serving items, a dummy resource...
|
222
|
+
# Also serving things, which just redirect to items...
|
223
|
+
#
|
224
|
+
class ItemServer
|
225
|
+
|
226
|
+
def initialize (args={})
|
227
|
+
|
228
|
+
port = args[:port] || 7777
|
229
|
+
|
230
|
+
#al = [
|
231
|
+
# [ "", WEBrick::AccessLog::COMMON_LOG_FORMAT ],
|
232
|
+
# [ "", WEBrick::AccessLog::REFERER_LOG_FORMAT ]]
|
233
|
+
|
234
|
+
@server = WEBrick::HTTPServer.new :Port => port, :AccessLog => nil
|
235
|
+
|
236
|
+
class << @server
|
237
|
+
attr_accessor :auth
|
238
|
+
end
|
239
|
+
|
240
|
+
@server.auth = args[:auth]
|
241
|
+
|
242
|
+
@server.mount "/items", ItemServlet
|
243
|
+
@server.mount "/things", ThingServlet
|
244
|
+
|
245
|
+
[ 'INT', 'TERM' ].each do |signal|
|
246
|
+
trap(signal) { shutdown }
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def start
|
251
|
+
|
252
|
+
Thread.new { @server.start }
|
253
|
+
# else the server and the test lock each other
|
254
|
+
end
|
255
|
+
|
256
|
+
def shutdown
|
257
|
+
|
258
|
+
ItemServlet.flush
|
259
|
+
@server.shutdown
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|