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>
|
<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>
|
<tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment>
|
||||||
<metal:edit define-macro="edit">
|
<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 StringIO import StringIO
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from base64 import encodestring
|
from base64 import encodestring
|
||||||
from appy.shared.utils import copyData
|
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)
|
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
|
||||||
binaryRex = re.compile(r'[\000-\006\177-\277]')
|
binaryRex = re.compile(r'[\000-\006\177-\277]')
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
class Resource:
|
class Resource:
|
||||||
'''Every instance of this class represents some web resource accessible
|
'''Every instance of this class represents some web resource accessible
|
||||||
through WebDAV.'''
|
through WebDAV.'''
|
||||||
|
|
||||||
def __init__(self, url, username=None, password=None):
|
def __init__(self, url, username=None, password=None, measure=False):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.url = url
|
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
|
# Split the URL into its components
|
||||||
res = urlRex.match(url)
|
res = urlRex.match(url)
|
||||||
if res:
|
if res:
|
||||||
|
@ -50,16 +115,29 @@ class Resource:
|
||||||
'''Sends a HTTP request with p_method, for p_uri.'''
|
'''Sends a HTTP request with p_method, for p_uri.'''
|
||||||
conn = httplib.HTTP()
|
conn = httplib.HTTP()
|
||||||
conn.connect(self.host, self.port)
|
conn.connect(self.host, self.port)
|
||||||
|
# Tell what kind of HTTP request it will be.
|
||||||
conn.putrequest(method, uri)
|
conn.putrequest(method, uri)
|
||||||
# Add HTTP headers
|
# Add HTTP headers
|
||||||
self.updateHeaders(headers)
|
self.updateHeaders(headers)
|
||||||
|
if self.headers: headers.update(self.headers)
|
||||||
for n, v in headers.items(): conn.putheader(n, v)
|
for n, v in headers.items(): conn.putheader(n, v)
|
||||||
conn.endheaders()
|
conn.endheaders()
|
||||||
if body: copyData(body, conn, 'send', type=bodyType)
|
# Add HTTP body
|
||||||
ver, code, msg = conn.getreply()
|
if body:
|
||||||
data = conn.getfile().read()
|
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()
|
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):
|
def mkdir(self, name):
|
||||||
'''Creates a folder named p_name in this resource.'''
|
'''Creates a folder named p_name in this resource.'''
|
||||||
|
@ -105,4 +183,32 @@ class Resource:
|
||||||
res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
|
res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
|
||||||
# Close the file when relevant
|
# Close the file when relevant
|
||||||
if type =='fileName': body.close()
|
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')
|
that is compatible with the content of p_data, ie file('myFile.doc','wb')
|
||||||
if content is binary.'''
|
if content is binary.'''
|
||||||
dump = getattr(target, targetMethod)
|
dump = getattr(target, targetMethod)
|
||||||
if type == 'string': dump(encodeData(data, encoding))
|
if not type or (type == 'string'): dump(encodeData(data, encoding))
|
||||||
elif type == 'file':
|
elif type == 'file':
|
||||||
while True:
|
while True:
|
||||||
chunk = data.read(chunkSize)
|
chunk = data.read(chunkSize)
|
||||||
|
|
Loading…
Reference in a new issue