dav4rack 0.2.11 → 0.3.0
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.rdoc +109 -0
- data/README.rdoc +8 -0
- data/dav4rack.gemspec +1 -22
- data/lib/dav4rack.rb +5 -0
- data/lib/dav4rack/controller.rb +123 -59
- data/lib/dav4rack/file.rb +1 -1
- data/lib/dav4rack/file_resource_lock.rb +159 -0
- data/lib/dav4rack/handler.rb +1 -1
- data/lib/dav4rack/resource.rb +25 -9
- data/lib/dav4rack/resources/file_resource.rb +366 -0
- data/lib/dav4rack/resources/mongo_resource.rb +341 -0
- data/lib/dav4rack/utils.rb +40 -0
- data/lib/dav4rack/version.rb +1 -1
- metadata +40 -24
- data/.gitignore +0 -7
- data/lib/dav4rack/file_resource.rb +0 -257
- data/spec/handler_spec.rb +0 -301
data/.gitignore
DELETED
@@ -1,257 +0,0 @@
|
|
1
|
-
require 'webrick/httputils'
|
2
|
-
|
3
|
-
module DAV4Rack
|
4
|
-
|
5
|
-
class FileResource < Resource
|
6
|
-
|
7
|
-
include WEBrick::HTTPUtils
|
8
|
-
|
9
|
-
# If this is a collection, return the child resources.
|
10
|
-
def children
|
11
|
-
Dir[file_path + '/*'].map do |path|
|
12
|
-
child File.basename(path)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# Is this resource a collection?
|
17
|
-
def collection?
|
18
|
-
File.directory?(file_path)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Does this recource exist?
|
22
|
-
def exist?
|
23
|
-
File.exist?(file_path)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Return the creation time.
|
27
|
-
def creation_date
|
28
|
-
stat.ctime
|
29
|
-
end
|
30
|
-
|
31
|
-
# Return the time of last modification.
|
32
|
-
def last_modified
|
33
|
-
stat.mtime
|
34
|
-
end
|
35
|
-
|
36
|
-
# Set the time of last modification.
|
37
|
-
def last_modified=(time)
|
38
|
-
File.utime(Time.now, time, file_path)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Return an Etag, an unique hash value for this resource.
|
42
|
-
def etag
|
43
|
-
sprintf('%x-%x-%x', stat.ino, stat.size, stat.mtime.to_i)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Return the mime type of this resource.
|
47
|
-
def content_type
|
48
|
-
if stat.directory?
|
49
|
-
"text/html"
|
50
|
-
else
|
51
|
-
mime_type(file_path, DefaultMimeTypes)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Return the size in bytes for this resource.
|
56
|
-
def content_length
|
57
|
-
stat.size
|
58
|
-
end
|
59
|
-
|
60
|
-
# HTTP GET request.
|
61
|
-
#
|
62
|
-
# Write the content of the resource to the response.body.
|
63
|
-
def get(request, response)
|
64
|
-
raise NotFound unless exist?
|
65
|
-
if stat.directory?
|
66
|
-
response.body = ""
|
67
|
-
Rack::Directory.new(root).call(request.env)[2].each do |line|
|
68
|
-
response.body << line
|
69
|
-
end
|
70
|
-
response['Content-Length'] = response.body.bytesize.to_s
|
71
|
-
else
|
72
|
-
file = Rack::File.new(root)
|
73
|
-
response.body = file
|
74
|
-
end
|
75
|
-
OK
|
76
|
-
end
|
77
|
-
|
78
|
-
# HTTP PUT request.
|
79
|
-
#
|
80
|
-
# Save the content of the request.body.
|
81
|
-
def put(request, response)
|
82
|
-
write(request.body)
|
83
|
-
Created
|
84
|
-
end
|
85
|
-
|
86
|
-
# HTTP POST request.
|
87
|
-
#
|
88
|
-
# Usually forbidden.
|
89
|
-
def post(request, response)
|
90
|
-
raise HTTPStatus::Forbidden
|
91
|
-
end
|
92
|
-
|
93
|
-
# HTTP DELETE request.
|
94
|
-
#
|
95
|
-
# Delete this resource.
|
96
|
-
def delete
|
97
|
-
if stat.directory?
|
98
|
-
FileUtils.rm_rf(file_path)
|
99
|
-
else
|
100
|
-
File.unlink(file_path)
|
101
|
-
end
|
102
|
-
NoContent
|
103
|
-
end
|
104
|
-
|
105
|
-
# HTTP COPY request.
|
106
|
-
#
|
107
|
-
# Copy this resource to given destination resource.
|
108
|
-
# Copy this resource to given destination resource.
|
109
|
-
def copy(dest, overwrite)
|
110
|
-
if(collection?)
|
111
|
-
if(dest.exist?)
|
112
|
-
if(dest.collection? && overwrite)
|
113
|
-
FileUtils.cp_r(file_path, dest.send(:file_path))
|
114
|
-
Created
|
115
|
-
else
|
116
|
-
if(overwrite)
|
117
|
-
FileUtils.rm(dest.send(:file_path))
|
118
|
-
FileUtils.cp_r(file_path, dest.send(:file_path))
|
119
|
-
NoContent
|
120
|
-
else
|
121
|
-
PreconditionFailed
|
122
|
-
end
|
123
|
-
end
|
124
|
-
else
|
125
|
-
FileUtils.cp_r(file_path, dest.send(:file_path))
|
126
|
-
Created
|
127
|
-
end
|
128
|
-
else
|
129
|
-
if(dest.exist? && !overwrite)
|
130
|
-
PreconditionFailed
|
131
|
-
else
|
132
|
-
if(File.directory?(File.dirname(dest.send(:file_path))))
|
133
|
-
new = !dest.exist?
|
134
|
-
if(dest.collection? && dest.exist?)
|
135
|
-
FileUtils.rm_rf(dest.send(:file_path))
|
136
|
-
end
|
137
|
-
FileUtils.cp(file_path, dest.send(:file_path).sub(/\/$/, ''))
|
138
|
-
new ? Created : NoContent
|
139
|
-
else
|
140
|
-
Conflict
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# HTTP MOVE request.
|
147
|
-
#
|
148
|
-
# Move this resource to given destination resource.
|
149
|
-
def move(*args)
|
150
|
-
result = copy(*args)
|
151
|
-
delete if [Created, NoContent].include?(result)
|
152
|
-
result
|
153
|
-
end
|
154
|
-
|
155
|
-
# HTTP MKCOL request.
|
156
|
-
#
|
157
|
-
# Create this resource as collection.
|
158
|
-
def make_collection
|
159
|
-
if(request.body.read.to_s == '')
|
160
|
-
if(File.directory?(file_path))
|
161
|
-
MethodNotAllowed
|
162
|
-
else
|
163
|
-
if(File.directory?(File.dirname(file_path)))
|
164
|
-
Dir.mkdir(file_path)
|
165
|
-
Created
|
166
|
-
else
|
167
|
-
Conflict
|
168
|
-
end
|
169
|
-
end
|
170
|
-
else
|
171
|
-
UnsupportedMediaType
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Write to this resource from given IO.
|
176
|
-
def write(io)
|
177
|
-
tempfile = "#{file_path}.#{Process.pid}.#{object_id}"
|
178
|
-
|
179
|
-
open(tempfile, "wb") do |file|
|
180
|
-
while part = io.read(8192)
|
181
|
-
file << part
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
File.rename(tempfile, file_path)
|
186
|
-
ensure
|
187
|
-
File.unlink(tempfile) rescue nil
|
188
|
-
end
|
189
|
-
|
190
|
-
# name:: String - Property name
|
191
|
-
# Returns the value of the given property
|
192
|
-
def get_property(name)
|
193
|
-
super || custom_props(name)
|
194
|
-
end
|
195
|
-
|
196
|
-
# name:: String - Property name
|
197
|
-
# value:: New value
|
198
|
-
# Set the property to the given value
|
199
|
-
def set_property(name, value)
|
200
|
-
super || set_custom_props(name,value)
|
201
|
-
end
|
202
|
-
|
203
|
-
protected
|
204
|
-
|
205
|
-
def set_custom_props(key,val)
|
206
|
-
prop_hash[key.to_sym] = val
|
207
|
-
File.open(prop_path, 'w') do |file|
|
208
|
-
file.write(YAML.dump(prop_hash))
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def custom_props(key)
|
213
|
-
prop_hash[key.to_sym]
|
214
|
-
end
|
215
|
-
|
216
|
-
def prop_path
|
217
|
-
path = File.join(root, '.props', File.dirname(file_path), File.basename(file_path))
|
218
|
-
unless(File.directory?(File.dirname(path)))
|
219
|
-
FileUtils.mkdir_p(File.dirname(path))
|
220
|
-
end
|
221
|
-
path
|
222
|
-
end
|
223
|
-
|
224
|
-
def prop_hash
|
225
|
-
unless(@_prop_hash)
|
226
|
-
if(File.exists?(prop_path))
|
227
|
-
@_prop_hash = YAML.load(File.read(prop_path))
|
228
|
-
else
|
229
|
-
@_prop_hash = {}
|
230
|
-
end
|
231
|
-
end
|
232
|
-
@_prop_hash
|
233
|
-
end
|
234
|
-
|
235
|
-
def authenticate(user, pass)
|
236
|
-
if(options[:username])
|
237
|
-
options[:username] == user && options[:password] == pass
|
238
|
-
else
|
239
|
-
true
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
def root
|
244
|
-
@options[:root]
|
245
|
-
end
|
246
|
-
|
247
|
-
def file_path
|
248
|
-
File.join(root, path)
|
249
|
-
end
|
250
|
-
|
251
|
-
def stat
|
252
|
-
@stat ||= File.stat(file_path)
|
253
|
-
end
|
254
|
-
|
255
|
-
end
|
256
|
-
|
257
|
-
end
|
data/spec/handler_spec.rb
DELETED
@@ -1,301 +0,0 @@
|
|
1
|
-
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'dav4rack'
|
5
|
-
require 'fileutils'
|
6
|
-
require 'nokogiri'
|
7
|
-
require 'rspec'
|
8
|
-
|
9
|
-
describe DAV4Rack::Handler do
|
10
|
-
DOC_ROOT = File.expand_path(File.dirname(__FILE__) + '/htdocs')
|
11
|
-
METHODS = %w(GET PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE OPTIONS HEAD LOCK UNLOCK)
|
12
|
-
|
13
|
-
before do
|
14
|
-
FileUtils.mkdir(DOC_ROOT) unless File.exists?(DOC_ROOT)
|
15
|
-
@controller = DAV4Rack::Handler.new(:root => DOC_ROOT)
|
16
|
-
end
|
17
|
-
|
18
|
-
after do
|
19
|
-
FileUtils.rm_rf(DOC_ROOT) if File.exists?(DOC_ROOT)
|
20
|
-
end
|
21
|
-
|
22
|
-
attr_reader :response
|
23
|
-
|
24
|
-
def request(method, uri, options={})
|
25
|
-
options = {
|
26
|
-
'HTTP_HOST' => 'localhost',
|
27
|
-
'REMOTE_USER' => 'user'
|
28
|
-
}.merge(options)
|
29
|
-
request = Rack::MockRequest.new(@controller)
|
30
|
-
@response = request.request(method, uri, options)
|
31
|
-
end
|
32
|
-
|
33
|
-
METHODS.each do |method|
|
34
|
-
define_method(method.downcase) do |*args|
|
35
|
-
request(method, *args)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def render(root_type)
|
40
|
-
raise ArgumentError.new 'Expecting block' unless block_given?
|
41
|
-
doc = Nokogiri::XML::Builder.new do |xml_base|
|
42
|
-
xml_base.send(root_type.to_s, 'xmlns:D' => 'D:') do
|
43
|
-
xml_base.parent.namespace = xml_base.parent.namespace_definitions.first
|
44
|
-
xml = xml_base['D']
|
45
|
-
yield xml
|
46
|
-
end
|
47
|
-
end
|
48
|
-
doc.to_xml
|
49
|
-
end
|
50
|
-
|
51
|
-
def url_escape(string)
|
52
|
-
URI.escape(string)
|
53
|
-
end
|
54
|
-
|
55
|
-
def response_xml
|
56
|
-
Nokogiri.XML(@response.body)
|
57
|
-
end
|
58
|
-
|
59
|
-
def multistatus_response(pattern)
|
60
|
-
@response.should be_multi_status
|
61
|
-
response_xml.xpath('//D:multistatus/D:response', response_xml.root.namespaces).should_not be_empty
|
62
|
-
response_xml.xpath("//D:multistatus/D:response#{pattern}", response_xml.root.namespaces)
|
63
|
-
end
|
64
|
-
|
65
|
-
def multi_status_created
|
66
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
67
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /Created/
|
68
|
-
end
|
69
|
-
|
70
|
-
def multi_status_ok
|
71
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
72
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /OK/
|
73
|
-
end
|
74
|
-
|
75
|
-
def multi_status_no_content
|
76
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
77
|
-
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /No Content/
|
78
|
-
end
|
79
|
-
|
80
|
-
def propfind_xml(*props)
|
81
|
-
render(:propfind) do |xml|
|
82
|
-
xml.prop do
|
83
|
-
props.each do |prop|
|
84
|
-
xml.send(prop.to_sym)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'should return all options' do
|
91
|
-
options('/').should be_ok
|
92
|
-
|
93
|
-
METHODS.each do |method|
|
94
|
-
response.headers['allow'].should include(method)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'should return headers' do
|
99
|
-
put('/test.html', :input => '<html/>').should be_created
|
100
|
-
head('/test.html').should be_ok
|
101
|
-
|
102
|
-
response.headers['etag'].should_not be_nil
|
103
|
-
response.headers['content-type'].should match(/html/)
|
104
|
-
response.headers['last-modified'].should_not be_nil
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'should not find a nonexistent resource' do
|
108
|
-
get('/not_found').should be_not_found
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'should not allow directory traversal' do
|
112
|
-
get('/../htdocs').should be_forbidden
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'should create a resource and allow its retrieval' do
|
116
|
-
put('/test', :input => 'body').should be_created
|
117
|
-
get('/test').should be_ok
|
118
|
-
response.body.should == 'body'
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'should return an absolute url after a put request' do
|
122
|
-
put('/test', :input => 'body').should be_created
|
123
|
-
response['location'].should =~ /http:\/\/localhost(:\d+)?\/test/
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'should create and find a url with escaped characters' do
|
127
|
-
put(url_escape('/a b'), :input => 'body').should be_created
|
128
|
-
get(url_escape('/a b')).should be_ok
|
129
|
-
response.body.should == 'body'
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'should delete a single resource' do
|
133
|
-
put('/test', :input => 'body').should be_created
|
134
|
-
delete('/test').should be_no_content
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'should delete recursively' do
|
138
|
-
mkcol('/folder').should be_created
|
139
|
-
put('/folder/a', :input => 'body').should be_created
|
140
|
-
put('/folder/b', :input => 'body').should be_created
|
141
|
-
|
142
|
-
delete('/folder').should be_no_content
|
143
|
-
get('/folder').should be_not_found
|
144
|
-
get('/folder/a').should be_not_found
|
145
|
-
get('/folder/b').should be_not_found
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'should not allow copy to another domain' do
|
149
|
-
put('/test', :input => 'body').should be_created
|
150
|
-
copy('http://localhost/', 'HTTP_DESTINATION' => 'http://another/').should be_bad_gateway
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'should not allow copy to the same resource' do
|
154
|
-
put('/test', :input => 'body').should be_created
|
155
|
-
copy('/test', 'HTTP_DESTINATION' => '/test').should be_forbidden
|
156
|
-
end
|
157
|
-
|
158
|
-
it 'should copy a single resource' do
|
159
|
-
put('/test', :input => 'body').should be_created
|
160
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy').should be_created
|
161
|
-
get('/copy').body.should == 'body'
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'should copy a resource with escaped characters' do
|
165
|
-
put(url_escape('/a b'), :input => 'body').should be_created
|
166
|
-
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c')).should be_created
|
167
|
-
get(url_escape('/a c')).should be_ok
|
168
|
-
response.body.should == 'body'
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'should deny a copy without overwrite' do
|
172
|
-
put('/test', :input => 'body').should be_created
|
173
|
-
put('/copy', :input => 'copy').should be_created
|
174
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F').should be_precondition_failed
|
175
|
-
get('/copy').body.should == 'copy'
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'should allow a copy with overwrite' do
|
179
|
-
put('/test', :input => 'body').should be_created
|
180
|
-
put('/copy', :input => 'copy').should be_created
|
181
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T').should be_no_content
|
182
|
-
get('/copy').body.should == 'body'
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'should copy a collection' do
|
186
|
-
mkcol('/folder').should be_created
|
187
|
-
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
188
|
-
multi_status_created.should eq true
|
189
|
-
propfind('/copy', :input => propfind_xml(:resourcetype))
|
190
|
-
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'should copy a collection resursively' do
|
194
|
-
mkcol('/folder').should be_created
|
195
|
-
put('/folder/a', :input => 'A').should be_created
|
196
|
-
put('/folder/b', :input => 'B').should be_created
|
197
|
-
|
198
|
-
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
199
|
-
multi_status_created.should eq true
|
200
|
-
propfind('/copy', :input => propfind_xml(:resourcetype))
|
201
|
-
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
202
|
-
get('/copy/a').body.should == 'A'
|
203
|
-
get('/copy/b').body.should == 'B'
|
204
|
-
end
|
205
|
-
|
206
|
-
it 'should move a collection recursively' do
|
207
|
-
mkcol('/folder').should be_created
|
208
|
-
put('/folder/a', :input => 'A').should be_created
|
209
|
-
put('/folder/b', :input => 'B').should be_created
|
210
|
-
|
211
|
-
move('/folder', 'HTTP_DESTINATION' => '/move')
|
212
|
-
multi_status_created.should eq true
|
213
|
-
propfind('/move', :input => propfind_xml(:resourcetype))
|
214
|
-
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
215
|
-
|
216
|
-
get('/move/a').body.should == 'A'
|
217
|
-
get('/move/b').body.should == 'B'
|
218
|
-
get('/folder/a').should be_not_found
|
219
|
-
get('/folder/b').should be_not_found
|
220
|
-
end
|
221
|
-
|
222
|
-
it 'should create a collection' do
|
223
|
-
mkcol('/folder').should be_created
|
224
|
-
propfind('/folder', :input => propfind_xml(:resourcetype))
|
225
|
-
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
226
|
-
end
|
227
|
-
|
228
|
-
it 'should return full urls after creating a collection' do
|
229
|
-
mkcol('/folder').should be_created
|
230
|
-
propfind('/folder', :input => propfind_xml(:resourcetype))
|
231
|
-
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
232
|
-
multistatus_response('/D:href').first.text.should =~ /http:\/\/localhost(:\d+)?\/folder/
|
233
|
-
end
|
234
|
-
|
235
|
-
it 'should not find properties for nonexistent resources' do
|
236
|
-
propfind('/non').should be_not_found
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'should find all properties' do
|
240
|
-
xml = render(:propfind) do |xml|
|
241
|
-
xml.allprop
|
242
|
-
end
|
243
|
-
|
244
|
-
propfind('http://localhost/', :input => xml)
|
245
|
-
|
246
|
-
multistatus_response('/D:href').first.text.strip.should =~ /http:\/\/localhost(:\d+)?\//
|
247
|
-
|
248
|
-
props = %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength)
|
249
|
-
props.each do |prop|
|
250
|
-
multistatus_response("/D:propstat/D:prop/D:#{prop}").should_not be_empty
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
it 'should find named properties' do
|
255
|
-
put('/test.html', :input => '<html/>').should be_created
|
256
|
-
propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
|
257
|
-
|
258
|
-
multistatus_response('/D:propstat/D:prop/D:getcontenttype').first.text.should == 'text/html'
|
259
|
-
multistatus_response('/D:propstat/D:prop/D:getcontentlength').first.text.should == '7'
|
260
|
-
end
|
261
|
-
|
262
|
-
it 'should lock a resource' do
|
263
|
-
put('/test', :input => 'body').should be_created
|
264
|
-
|
265
|
-
xml = render(:lockinfo) do |xml|
|
266
|
-
xml.lockscope { xml.exclusive }
|
267
|
-
xml.locktype { xml.write }
|
268
|
-
xml.owner { xml.href "http://test.de/" }
|
269
|
-
end
|
270
|
-
|
271
|
-
lock('/test', :input => xml)
|
272
|
-
|
273
|
-
response.should be_ok
|
274
|
-
|
275
|
-
match = lambda do |pattern|
|
276
|
-
response_xml.xpath "/D:prop/D:lockdiscovery/D:activelock#{pattern}"
|
277
|
-
end
|
278
|
-
|
279
|
-
match[''].should_not be_empty
|
280
|
-
|
281
|
-
match['/D:locktype'].should_not be_empty
|
282
|
-
match['/D:lockscope'].should_not be_empty
|
283
|
-
match['/D:depth'].should_not be_empty
|
284
|
-
match['/D:timeout'].should_not be_empty
|
285
|
-
match['/D:locktoken'].should_not be_empty
|
286
|
-
match['/D:owner'].should_not be_empty
|
287
|
-
end
|
288
|
-
|
289
|
-
context "when mapping a path" do
|
290
|
-
|
291
|
-
before do
|
292
|
-
@controller = DAV4Rack::Handler.new(:root => DOC_ROOT, :root_uri_path => '/webdav/')
|
293
|
-
end
|
294
|
-
|
295
|
-
it "should return correct urls" do
|
296
|
-
# FIXME: a put to '/test' works, too -- should it?
|
297
|
-
put('/webdav/test', :input => 'body').should be_created
|
298
|
-
response.headers['location'].should =~ /http:\/\/localhost(:\d+)?\/webdav\/test/
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|