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
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user