Completed the WebDav library with standard GET and POST HTTP requests. MultiPart not yet implemented.
This commit is contained in:
parent
2aedf8c88a
commit
feca97bda3
|
@ -1,5 +1,7 @@
|
|||
<tal:comment replace="nothing">View macro for a Boolean.</tal:comment>
|
||||
<metal:view define-macro="view"><span tal:replace="value"/></metal:view>
|
||||
<metal:view define-macro="view">
|
||||
<span tal:attributes="class widget/master_css; id rawValue" tal:content="value"></span>
|
||||
</metal:view>
|
||||
|
||||
<tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment>
|
||||
<metal:edit define-macro="edit">
|
||||
|
|
122
shared/dav.py
122
shared/dav.py
|
@ -1,24 +1,89 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import os, re, httplib, sys, stat
|
||||
import os, re, httplib, sys, stat, urlparse, time
|
||||
from urllib import quote
|
||||
from StringIO import StringIO
|
||||
from mimetypes import guess_type
|
||||
from base64 import encodestring
|
||||
from appy.shared.utils import copyData
|
||||
from appy.gen.utils import sequenceTypes
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class DataEncoder:
|
||||
'''Allows to encode form data for sending it through a HTTP request.'''
|
||||
def __init__(self, data):
|
||||
self.data = data # The data to encode, as a dict
|
||||
|
||||
def marshalValue(self, name, value):
|
||||
if isinstance(value, basestring):
|
||||
return '%s=%s' % (name, quote(str(value)))
|
||||
elif isinstance(value, float):
|
||||
return '%s:float=%s' % (name, value)
|
||||
elif isinstance(value, int):
|
||||
return '%s:int=%s' % (name, value)
|
||||
elif isinstance(value, long):
|
||||
res = '%s:long=%s' % (name, value)
|
||||
if res[-1] == 'L':
|
||||
res = res[:-1]
|
||||
return res
|
||||
else:
|
||||
raise 'Cannot encode value %s' % str(value)
|
||||
|
||||
def encode(self):
|
||||
res = []
|
||||
for name, value in self.data.iteritems():
|
||||
res.append(self.marshalValue(name, value))
|
||||
return '&'.join(res)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class HttpResponse:
|
||||
'''Stores information about a HTTP response.'''
|
||||
def __init__(self, code, text, headers, body, duration=None):
|
||||
self.code = code # The return code, ie 404, 200, ...
|
||||
self.text = text # Textual description of the code
|
||||
self.headers = headers # A dict-like object containing the headers
|
||||
self.body = body # The body of the HTTP response
|
||||
# The following attribute may contain specific data extracted from
|
||||
# the previous fields. For example, when response if 302 (Redirect),
|
||||
# self.data contains the URI where we must redirect the user to.
|
||||
self.data = self.extractData()
|
||||
# p_duration, if given, is the time, in seconds, we have waited, before
|
||||
# getting this response after having sent the request.
|
||||
self.duration = duration
|
||||
|
||||
def __repr__(self):
|
||||
duration = ''
|
||||
if self.duration: duration = ', got in %.4f seconds' % self.duration
|
||||
return '<HttpResponse %s (%s)%s>' % (self.code, self.text, duration)
|
||||
|
||||
def extractData(self):
|
||||
'''This method extracts, from the various parts of the HTTP response,
|
||||
some useful information. For example, it will find the URI where to
|
||||
redirect the user to if self.code is 302.'''
|
||||
if self.code == 302:
|
||||
return urlparse.urlparse(self.headers['location'])[2]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
|
||||
binaryRex = re.compile(r'[\000-\006\177-\277]')
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Resource:
|
||||
'''Every instance of this class represents some web resource accessible
|
||||
through WebDAV.'''
|
||||
|
||||
def __init__(self, url, username=None, password=None):
|
||||
def __init__(self, url, username=None, password=None, measure=False):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.url = url
|
||||
|
||||
# If some headers must be sent with any request sent through this
|
||||
# resource (like a cookie), you can store them in the following dict.
|
||||
self.headers = {}
|
||||
# If p_measure is True, we will measure, for every request sent, the
|
||||
# time we wait until we receive the response.
|
||||
self.measure = measure
|
||||
# If measure is True, we will store hereafter, the total time (in
|
||||
# seconds) spent waiting for the server for all requests sent through
|
||||
# this resource object.
|
||||
self.serverTime = 0
|
||||
# Split the URL into its components
|
||||
res = urlRex.match(url)
|
||||
if res:
|
||||
|
@ -50,16 +115,29 @@ class Resource:
|
|||
'''Sends a HTTP request with p_method, for p_uri.'''
|
||||
conn = httplib.HTTP()
|
||||
conn.connect(self.host, self.port)
|
||||
# Tell what kind of HTTP request it will be.
|
||||
conn.putrequest(method, uri)
|
||||
# Add HTTP headers
|
||||
self.updateHeaders(headers)
|
||||
if self.headers: headers.update(self.headers)
|
||||
for n, v in headers.items(): conn.putheader(n, v)
|
||||
conn.endheaders()
|
||||
if body: copyData(body, conn, 'send', type=bodyType)
|
||||
ver, code, msg = conn.getreply()
|
||||
data = conn.getfile().read()
|
||||
# Add HTTP body
|
||||
if body:
|
||||
if not bodyType: bodyType = 'string'
|
||||
copyData(body, conn, 'send', type=bodyType)
|
||||
# Send the request, get the reply
|
||||
if self.measure: startTime = time.time()
|
||||
code, text, headers = conn.getreply()
|
||||
if self.measure: endTime = time.time()
|
||||
body = conn.getfile().read()
|
||||
conn.close()
|
||||
return data
|
||||
# Return a smart object containing the various parts of the response
|
||||
duration = None
|
||||
if self.measure:
|
||||
duration = endTime - startTime
|
||||
self.serverTime += duration
|
||||
return HttpResponse(code, text, headers, body, duration=duration)
|
||||
|
||||
def mkdir(self, name):
|
||||
'''Creates a folder named p_name in this resource.'''
|
||||
|
@ -105,4 +183,32 @@ class Resource:
|
|||
res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
|
||||
# Close the file when relevant
|
||||
if type =='fileName': body.close()
|
||||
return res
|
||||
|
||||
def _encodeFormData(self, data):
|
||||
'''Returns the encoded form p_data.'''
|
||||
res = []
|
||||
for name, value in data.items():
|
||||
n = name.rfind( '__')
|
||||
if n > 0:
|
||||
tag = name[n+2:]
|
||||
key = name[:n]
|
||||
else: tag = 'string'
|
||||
func = varfuncs.get(tag, marshal_string)
|
||||
res.append(func(name, value))
|
||||
return '&'.join(res)
|
||||
|
||||
def get(self, uri=None, headers={}):
|
||||
'''Perform a HTTP GET on the server.'''
|
||||
if not uri: uri = self.uri
|
||||
return self.sendRequest('GET', uri, headers=headers)
|
||||
|
||||
def post(self, data, uri=None, headers={}):
|
||||
'''Perform a HTTP POST on the server.'''
|
||||
if not uri: uri = self.uri
|
||||
# Format the form data and prepare headers
|
||||
body = DataEncoder(data).encode()
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
headers['Content-Length'] = str(len(body))
|
||||
return self.sendRequest('POST', uri, headers=headers, body=body)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -95,7 +95,7 @@ def copyData(data, target, targetMethod, type='string', encoding=None,
|
|||
that is compatible with the content of p_data, ie file('myFile.doc','wb')
|
||||
if content is binary.'''
|
||||
dump = getattr(target, targetMethod)
|
||||
if type == 'string': dump(encodeData(data, encoding))
|
||||
if not type or (type == 'string'): dump(encodeData(data, encoding))
|
||||
elif type == 'file':
|
||||
while True:
|
||||
chunk = data.read(chunkSize)
|
||||
|
|
Loading…
Reference in a new issue