summaryrefslogtreecommitdiff
path: root/addons/document_webdav
diff options
context:
space:
mode:
authorP. Christeas <p_christ@hol.gr>2010-11-03 13:25:35 +0200
committerP. Christeas <p_christ@hol.gr>2010-11-03 13:25:35 +0200
commit1c5c26a8afaed89d83fcaaa5aa6fb9a31d26a6c8 (patch)
tree088c6e9cf87fd261f35b186808855aff1b2a9e1e /addons/document_webdav
parent7f882e14cf2948a4fe4b2b2b34fa085e6dba4278 (diff)
doc webdav: move to the node_descriptor API
We need to support chunked transfers, as well as Ranges, so we should no longer use the node.get_data() but node.open_data('r'). bzr revid: p_christ@hol.gr-20101103112535-34sr4dhd3neunfv1
Diffstat (limited to 'addons/document_webdav')
-rw-r--r--addons/document_webdav/dav_fs.py109
-rw-r--r--addons/document_webdav/test/webdav_test1.yml2
-rw-r--r--addons/document_webdav/test_davclient.py9
3 files changed, 114 insertions, 6 deletions
diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py
index 3e8e1c2b3e5..ebd3fa6b7cc 100644
--- a/addons/document_webdav/dav_fs.py
+++ b/addons/document_webdav/dav_fs.py
@@ -24,6 +24,7 @@ import base64
import sys
import os
import time
+import errno
from string import joinfields, split, lower
import netsvc
@@ -74,6 +75,89 @@ def _str2time(cre):
cre = cre[:fdot]
return time.mktime(time.strptime(cre,'%Y-%m-%d %H:%M:%S')) + frac
+class BoundStream2(object):
+ """Wraps around a seekable buffer, reads a determined range of data
+
+ Note that the supplied stream object MUST support a size() which
+ should return its data length (in bytes).
+
+ A variation of the class in websrv_lib.py
+ """
+
+ def __init__(self, stream, offset=None, length=None, chunk_size=None):
+ self._stream = stream
+ self._offset = offset or 0
+ self._length = length or self._stream.size()
+ self._rem_length = length
+ assert length and isinstance(length, (int, long))
+ assert length and length >= 0, length
+ self._chunk_size = chunk_size
+ if offset is not None:
+ self._stream.seek(offset)
+
+ def read(self, size=-1):
+ if not self._stream:
+ raise IOError(errno.EBADF, "read() without stream")
+
+ if self._rem_length == 0:
+ return ''
+ elif self._rem_length < 0:
+ raise EOFError()
+
+ rsize = self._rem_length
+ if size > 0 and size < rsize:
+ rsize = size
+ if self._chunk_size and self._chunk_size < rsize:
+ rsize = self._chunk_size
+
+ data = self._stream.read(rsize)
+ self._rem_length -= len(data)
+
+ return data
+
+ def __len__(self):
+ return self._length
+
+ def tell(self):
+ res = self._stream.tell()
+ if self._offset:
+ res -= self._offset
+ return res
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ return self.read(65536)
+
+ def seek(self, pos, whence=os.SEEK_SET):
+ """ Seek, computing our limited range
+ """
+ if whence == os.SEEK_SET:
+ if pos < 0 or pos > self._length:
+ raise IOError(errno.EINVAL,"Cannot seek")
+ self._stream.seek(pos - self._offset)
+ self._rem_length = self._length - pos
+ elif whence == os.SEEK_CUR:
+ if pos > 0:
+ if pos > self._rem_length:
+ raise IOError(errno.EINVAL,"Cannot seek past end")
+ elif pos < 0:
+ oldpos = self.tell()
+ if oldpos + pos < 0:
+ raise IOError(errno.EINVAL,"Cannot seek before start")
+ self._stream.seek(pos, os.SEEK_CUR)
+ self._rem_length -= pos
+ elif whence == os.SEEK_END:
+ if pos > 0:
+ raise IOError(errno.EINVAL,"Cannot seek past end")
+ else:
+ if self._length + pos < 0:
+ raise IOError(errno.EINVAL,"Cannot seek before start")
+ newpos = self._offset + self._length + pos
+ self._stream.seek(newpos, os.SEEK_SET)
+ self._rem_length = 0 - pos
+
class openerp_dav_handler(dav_interface):
"""
This class models a OpenERP interface for the DAV server
@@ -396,11 +480,28 @@ class openerp_dav_handler(dav_interface):
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound2(uri2)
+ # TODO: if node is a collection, for some specific set of
+ # clients ( web browsers; available in node context),
+ # we may return a pseydo-html page with the directory listing.
try:
+ res = node.open_data(cr,'r')
if rrange:
- self.parent.log_error("Doc get_data cannot use range")
- raise DAV_Error(409)
- datas = node.get_data(cr)
+ assert isinstance(rrange, (tuple,list))
+ start, end = map(long, rrange)
+ if not start:
+ start = 0
+ assert start >= 0
+ if end and end < start:
+ self.parent.log_error("Invalid range for data: %s-%s" %(start, end))
+ raise DAV_Error(416, "Invalid range for data")
+ if end:
+ if end >= res.size():
+ raise DAV_Error(416, "Requested data exceeds available size")
+ length = (end + 1) - start
+ else:
+ length = res.size() - start
+ res = BoundStream2(res, offset=start, length=length)
+
except TypeError,e:
# for the collections that return this error, the DAV standard
# says we'd better just return 200 OK with empty data
@@ -413,7 +514,7 @@ class openerp_dav_handler(dav_interface):
self.parent.log_error("GET exception: %s",str(e))
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Error, 409
- return str(datas) # FIXME!
+ return res
finally:
if cr: cr.close()
diff --git a/addons/document_webdav/test/webdav_test1.yml b/addons/document_webdav/test/webdav_test1.yml
index a64c2efae4c..a0f15c10fc9 100644
--- a/addons/document_webdav/test/webdav_test1.yml
+++ b/addons/document_webdav/test/webdav_test1.yml
@@ -56,4 +56,4 @@
dc = te.DAVClient()
dc.get_creds(self, cr, uid)
tdp = addons.get_module_resource('document_webdav', 'test_davclient.py')
- res = dc.gd_get(path=cr.dbname+'/Documents/test_davclient.py', compare=tdp)
+ res = dc.gd_get(path=cr.dbname+'/Documents/test_davclient.py', crange=(4,508), compare=tdp)
diff --git a/addons/document_webdav/test_davclient.py b/addons/document_webdav/test_davclient.py
index 35c870838ec..e5c3659d870 100644
--- a/addons/document_webdav/test_davclient.py
+++ b/addons/document_webdav/test_davclient.py
@@ -654,7 +654,9 @@ class DAVClient(object):
hdrs['Range'] = 'bytes='+ (','.join(rs))
s, m, d = self._http_request(self.davpath + path, method='GET', hdrs=hdrs)
assert s in (200, 206), "Bad status: %s" % s
- ctype = m.getheader('Content-Type').split(';',1)[0]
+ ctype = m.getheader('Content-Type')
+ if ctype and ';' in ctype:
+ ctype = ctype.split(';',1)[0]
if mime:
assert ctype == mime, m.getheader('Content-Type')
rrange = None
@@ -668,6 +670,11 @@ class DAVClient(object):
fd = open(compare, 'rb')
d2 = fd.read()
fd.close()
+ if crange:
+ if len(crange) > 1:
+ raise NotImplementedError
+ r = crange[0]
+ d2 = d2[r[0]:r[1]+1]
assert d2 == d, "Data does not match"
return ctype, rrange, d