diff options
| author | P. Christeas <p_christ@hol.gr> | 2010-11-03 13:25:35 +0200 |
|---|---|---|
| committer | P. Christeas <p_christ@hol.gr> | 2010-11-03 13:25:35 +0200 |
| commit | 1c5c26a8afaed89d83fcaaa5aa6fb9a31d26a6c8 (patch) | |
| tree | 088c6e9cf87fd261f35b186808855aff1b2a9e1e /addons/document_webdav | |
| parent | 7f882e14cf2948a4fe4b2b2b34fa085e6dba4278 (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.py | 109 | ||||
| -rw-r--r-- | addons/document_webdav/test/webdav_test1.yml | 2 | ||||
| -rw-r--r-- | addons/document_webdav/test_davclient.py | 9 |
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 |
